... | ... | ||
---|---|---|---|
20 | qemu instead. | 20 | qemu instead. |
21 | 21 | ||
22 | A edk2 test branch can be found here (build with "-D QEMU_VARS=TRUE"). | 22 | A edk2 test branch can be found here (build with "-D QEMU_VARS=TRUE"). |
23 | https://github.com/kraxel/edk2/commits/devel/secure-boot-external-vars | 23 | https://github.com/kraxel/edk2/commits/devel/secure-boot-external-vars |
24 | 24 | ||
25 | The uefi-vars device must re-implement the privileged edk2 protocols | 25 | The uefi-vars device re-implements the privileged edk2 protocols |
26 | (i.e. the code running in SMM mode). The implementation is not complete | 26 | (i.e. the code running in SMM mode). |
27 | yet, specifically updating authenticated variables is not implemented. | ||
28 | These variables are simply read-only for now. | ||
29 | 27 | ||
30 | But there is enough functionality working that it is possible to run | 28 | v2 changes: |
31 | guests, including guests in secure boot mode, so I'm sending this out | 29 | - fully implement authenticated variables. |
32 | for feedback (before tackling the remaining 20% which evidently will | 30 | - various cleanups and fixes. |
33 | need 80% of the time ;) | ||
34 | |||
35 | Because the guest can not write to authenticated variables (yet) it can | ||
36 | not enroll secure boot keys itself, this must be done on the host. The | ||
37 | virt-firmware tools (https://gitlab.com/kraxel/virt-firmware) can be | ||
38 | used for that: | ||
39 | |||
40 | virt-fw-vars --enroll-redhat --secure-boot --output-json uefivars.json | ||
41 | 31 | ||
42 | enjoy & take care, | 32 | enjoy & take care, |
43 | Gerd | 33 | Gerd |
44 | 34 | ||
45 | Gerd Hoffmann (16): | 35 | Gerd Hoffmann (21): |
46 | hw/uefi: add include/hw/uefi/var-service-api.h | 36 | hw/uefi: add include/hw/uefi/var-service-api.h |
47 | hw/uefi: add include/hw/uefi/var-service-edk2.h | 37 | hw/uefi: add include/hw/uefi/var-service-edk2.h |
48 | hw/uefi: add include/hw/uefi/var-service.h | 38 | hw/uefi: add include/hw/uefi/var-service.h |
49 | hw/uefi: add var-service-guid.c | 39 | hw/uefi: add var-service-guid.c |
50 | hw/uefi: add var-service-core.c | 40 | hw/uefi: add var-service-utils.c |
51 | hw/uefi: add var-service-vars.c | 41 | hw/uefi: add var-service-vars.c |
52 | hw/uefi: add var-service-auth.c | 42 | hw/uefi: add var-service-auth.c |
53 | hw/uefi: add var-service-policy.c | 43 | hw/uefi: add var-service-policy.c |
54 | hw/uefi: add support for storing persistent variables on disk | 44 | hw/uefi: add var-service-core.c |
45 | hw/uefi: add var-service-pkcs7.c | ||
46 | hw/uefi: add var-service-pkcs7-stub.c | ||
47 | hw/uefi: add var-service-siglist.c | ||
48 | hw/uefi: add var-service-json.c + qapi for NV vars. | ||
55 | hw/uefi: add trace-events | 49 | hw/uefi: add trace-events |
56 | hw/uefi: add to Kconfig | 50 | hw/uefi: add UEFI_VARS to Kconfig |
57 | hw/uefi: add to meson | 51 | hw/uefi: add to meson |
58 | hw/uefi: add uefi-vars-sysbus device | 52 | hw/uefi: add uefi-vars-sysbus device |
59 | hw/uefi: add uefi-vars-isa device | 53 | hw/uefi: add uefi-vars-isa device |
60 | hw/arm: add uefi variable support to virt machine type | 54 | hw/arm: add uefi variable support to virt machine type |
61 | docs: add uefi variable service documentation and TODO list. | 55 | docs: add uefi variable service documentation |
56 | hw/uefi: add MAINTAINERS entry | ||
62 | 57 | ||
63 | include/hw/arm/virt.h | 2 + | 58 | include/hw/arm/virt.h | 2 + |
64 | include/hw/uefi/var-service-api.h | 40 ++ | 59 | include/hw/uefi/var-service-api.h | 40 ++ |
65 | include/hw/uefi/var-service-edk2.h | 184 +++++++++ | 60 | include/hw/uefi/var-service-edk2.h | 227 +++++++++ |
66 | include/hw/uefi/var-service.h | 119 ++++++ | 61 | include/hw/uefi/var-service.h | 186 ++++++++ |
67 | hw/arm/virt.c | 41 ++ | 62 | hw/arm/virt.c | 41 ++ |
68 | hw/uefi/var-service-auth.c | 91 +++++ | 63 | hw/uefi/var-service-auth.c | 361 ++++++++++++++ |
69 | hw/uefi/var-service-core.c | 350 +++++++++++++++++ | 64 | hw/uefi/var-service-core.c | 237 ++++++++++ |
70 | hw/uefi/var-service-guid.c | 61 +++ | 65 | hw/uefi/var-service-guid.c | 99 ++++ |
71 | hw/uefi/var-service-isa.c | 88 +++++ | 66 | hw/uefi/var-service-isa.c | 91 ++++ |
72 | hw/uefi/var-service-json.c | 194 ++++++++++ | 67 | hw/uefi/var-service-json.c | 242 ++++++++++ |
73 | hw/uefi/var-service-policy.c | 390 +++++++++++++++++++ | 68 | hw/uefi/var-service-pkcs7-stub.c | 16 + |
74 | hw/uefi/var-service-sysbus.c | 87 +++++ | 69 | hw/uefi/var-service-pkcs7.c | 436 +++++++++++++++++ |
75 | hw/uefi/var-service-vars.c | 602 +++++++++++++++++++++++++++++ | 70 | hw/uefi/var-service-policy.c | 370 +++++++++++++++ |
71 | hw/uefi/var-service-siglist.c | 212 +++++++++ | ||
72 | hw/uefi/var-service-sysbus.c | 90 ++++ | ||
73 | hw/uefi/var-service-utils.c | 241 ++++++++++ | ||
74 | hw/uefi/var-service-vars.c | 725 +++++++++++++++++++++++++++++ | ||
75 | MAINTAINERS | 6 + | ||
76 | docs/devel/index-internals.rst | 1 + | 76 | docs/devel/index-internals.rst | 1 + |
77 | docs/devel/uefi-vars.rst | 66 ++++ | 77 | docs/devel/uefi-vars.rst | 66 +++ |
78 | hw/Kconfig | 1 + | 78 | hw/Kconfig | 1 + |
79 | hw/meson.build | 1 + | 79 | hw/meson.build | 1 + |
80 | hw/uefi/Kconfig | 9 + | 80 | hw/uefi/Kconfig | 9 + |
81 | hw/uefi/TODO.md | 17 + | 81 | hw/uefi/LIMITATIONS.md | 7 + |
82 | hw/uefi/meson.build | 18 + | 82 | hw/uefi/meson.build | 24 + |
83 | hw/uefi/trace-events | 16 + | 83 | hw/uefi/trace-events | 17 + |
84 | meson.build | 1 + | 84 | meson.build | 1 + |
85 | qapi/meson.build | 1 + | 85 | qapi/meson.build | 1 + |
86 | qapi/qapi-schema.json | 1 + | 86 | qapi/qapi-schema.json | 1 + |
87 | qapi/uefi.json | 40 ++ | 87 | qapi/uefi.json | 45 ++ |
88 | 25 files changed, 2421 insertions(+) | 88 | 30 files changed, 3796 insertions(+) |
89 | create mode 100644 include/hw/uefi/var-service-api.h | 89 | create mode 100644 include/hw/uefi/var-service-api.h |
90 | create mode 100644 include/hw/uefi/var-service-edk2.h | 90 | create mode 100644 include/hw/uefi/var-service-edk2.h |
91 | create mode 100644 include/hw/uefi/var-service.h | 91 | create mode 100644 include/hw/uefi/var-service.h |
92 | create mode 100644 hw/uefi/var-service-auth.c | 92 | create mode 100644 hw/uefi/var-service-auth.c |
93 | create mode 100644 hw/uefi/var-service-core.c | 93 | create mode 100644 hw/uefi/var-service-core.c |
94 | create mode 100644 hw/uefi/var-service-guid.c | 94 | create mode 100644 hw/uefi/var-service-guid.c |
95 | create mode 100644 hw/uefi/var-service-isa.c | 95 | create mode 100644 hw/uefi/var-service-isa.c |
96 | create mode 100644 hw/uefi/var-service-json.c | 96 | create mode 100644 hw/uefi/var-service-json.c |
97 | create mode 100644 hw/uefi/var-service-pkcs7-stub.c | ||
98 | create mode 100644 hw/uefi/var-service-pkcs7.c | ||
97 | create mode 100644 hw/uefi/var-service-policy.c | 99 | create mode 100644 hw/uefi/var-service-policy.c |
100 | create mode 100644 hw/uefi/var-service-siglist.c | ||
98 | create mode 100644 hw/uefi/var-service-sysbus.c | 101 | create mode 100644 hw/uefi/var-service-sysbus.c |
102 | create mode 100644 hw/uefi/var-service-utils.c | ||
99 | create mode 100644 hw/uefi/var-service-vars.c | 103 | create mode 100644 hw/uefi/var-service-vars.c |
100 | create mode 100644 docs/devel/uefi-vars.rst | 104 | create mode 100644 docs/devel/uefi-vars.rst |
101 | create mode 100644 hw/uefi/Kconfig | 105 | create mode 100644 hw/uefi/Kconfig |
102 | create mode 100644 hw/uefi/TODO.md | 106 | create mode 100644 hw/uefi/LIMITATIONS.md |
103 | create mode 100644 hw/uefi/meson.build | 107 | create mode 100644 hw/uefi/meson.build |
104 | create mode 100644 hw/uefi/trace-events | 108 | create mode 100644 hw/uefi/trace-events |
105 | create mode 100644 qapi/uefi.json | 109 | create mode 100644 qapi/uefi.json |
106 | 110 | ||
107 | -- | 111 | -- |
108 | 2.41.0 | 112 | 2.47.1 | diff view generated by jsdifflib |
1 | This file defines the register interface of the uefi-vars device. | 1 | This file defines the register interface of the uefi-vars device. |
---|---|---|---|
2 | It's only a handful of registers: magic value, command and status | 2 | It's only a handful of registers: magic value, command and status |
3 | registers, location and size of the communication buffer. | 3 | registers, location and size of the communication buffer. |
4 | 4 | ||
5 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 5 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
6 | Reviewed-by: Laszlo Ersek <lersek@redhat.com> | ||
6 | --- | 7 | --- |
7 | include/hw/uefi/var-service-api.h | 40 +++++++++++++++++++++++++++++++ | 8 | include/hw/uefi/var-service-api.h | 40 +++++++++++++++++++++++++++++++ |
8 | 1 file changed, 40 insertions(+) | 9 | 1 file changed, 40 insertions(+) |
9 | create mode 100644 include/hw/uefi/var-service-api.h | 10 | create mode 100644 include/hw/uefi/var-service-api.h |
10 | 11 | ||
... | ... | ||
53 | +#define UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE 0x12 | 54 | +#define UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE 0x12 |
54 | + | 55 | + |
55 | + | 56 | + |
56 | +#endif /* QEMU_UEFI_VAR_SERVICE_API_H */ | 57 | +#endif /* QEMU_UEFI_VAR_SERVICE_API_H */ |
57 | -- | 58 | -- |
58 | 2.41.0 | 59 | 2.47.1 | diff view generated by jsdifflib |
1 | A bunch of #defines and structs copied over from edk2, | 1 | A bunch of #defines and structs copied over from edk2, |
---|---|---|---|
2 | mostly needed to decode and encode the messages in the | 2 | mostly needed to decode and encode the messages in the |
3 | communication buffer. | 3 | communication buffer. |
4 | 4 | ||
5 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 5 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
6 | --- | 6 | --- |
7 | include/hw/uefi/var-service-edk2.h | 184 +++++++++++++++++++++++++++++ | 7 | include/hw/uefi/var-service-edk2.h | 227 +++++++++++++++++++++++++++++ |
8 | 1 file changed, 184 insertions(+) | 8 | 1 file changed, 227 insertions(+) |
9 | create mode 100644 include/hw/uefi/var-service-edk2.h | 9 | create mode 100644 include/hw/uefi/var-service-edk2.h |
10 | 10 | ||
11 | diff --git a/include/hw/uefi/var-service-edk2.h b/include/hw/uefi/var-service-edk2.h | 11 | diff --git a/include/hw/uefi/var-service-edk2.h b/include/hw/uefi/var-service-edk2.h |
12 | new file mode 100644 | 12 | new file mode 100644 |
13 | index XXXXXXX..XXXXXXX | 13 | index XXXXXXX..XXXXXXX |
... | ... | ||
37 | +#define EFI_WRITE_PROTECTED ENCODE_ERROR(8) | 37 | +#define EFI_WRITE_PROTECTED ENCODE_ERROR(8) |
38 | +#define EFI_OUT_OF_RESOURCES ENCODE_ERROR(9) | 38 | +#define EFI_OUT_OF_RESOURCES ENCODE_ERROR(9) |
39 | +#define EFI_NOT_FOUND ENCODE_ERROR(14) | 39 | +#define EFI_NOT_FOUND ENCODE_ERROR(14) |
40 | +#define EFI_ACCESS_DENIED ENCODE_ERROR(15) | 40 | +#define EFI_ACCESS_DENIED ENCODE_ERROR(15) |
41 | +#define EFI_ALREADY_STARTED ENCODE_ERROR(20) | 41 | +#define EFI_ALREADY_STARTED ENCODE_ERROR(20) |
42 | +#define EFI_SECURITY_VIOLATION ENCODE_ERROR(26) | ||
42 | + | 43 | + |
43 | +#define EFI_VARIABLE_NON_VOLATILE 0x01 | 44 | +#define EFI_VARIABLE_NON_VOLATILE 0x01 |
44 | +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x02 | 45 | +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x02 |
45 | +#define EFI_VARIABLE_RUNTIME_ACCESS 0x04 | 46 | +#define EFI_VARIABLE_RUNTIME_ACCESS 0x04 |
46 | +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x08 | 47 | +#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x08 |
47 | +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x10 // deprecated | 48 | +#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x10 /* deprecated */ |
48 | +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x20 | 49 | +#define EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS 0x20 |
49 | +#define EFI_VARIABLE_APPEND_WRITE 0x40 | 50 | +#define EFI_VARIABLE_APPEND_WRITE 0x40 |
50 | + | 51 | + |
51 | +/* SecureBootEnable */ | 52 | +/* SecureBootEnable */ |
52 | +#define SECURE_BOOT_ENABLE 1 | 53 | +#define SECURE_BOOT_ENABLE 1 |
... | ... | ||
194 | + uint8_t value; | 195 | + uint8_t value; |
195 | + uint8_t padding; | 196 | + uint8_t padding; |
196 | + /* Name */ | 197 | + /* Name */ |
197 | +}; | 198 | +}; |
198 | + | 199 | + |
200 | +/* --- variable authentication --------------------------------------- */ | ||
201 | + | ||
202 | +#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 | ||
203 | + | ||
204 | +typedef struct efi_time efi_time; | ||
205 | +typedef struct efi_siglist efi_siglist; | ||
206 | +typedef struct variable_auth_2 variable_auth_2; | ||
207 | + | ||
208 | +/* EFI_TIME */ | ||
209 | +struct efi_time { | ||
210 | + uint16_t year; | ||
211 | + uint8_t month; | ||
212 | + uint8_t day; | ||
213 | + uint8_t hour; | ||
214 | + uint8_t minute; | ||
215 | + uint8_t second; | ||
216 | + uint8_t pad1; | ||
217 | + uint32_t nanosecond; | ||
218 | + int16_t timezone; | ||
219 | + uint8_t daylight; | ||
220 | + uint8_t pad2; | ||
221 | +}; | ||
222 | + | ||
223 | +/* EFI_SIGNATURE_LIST */ | ||
224 | +struct efi_siglist { | ||
225 | + QemuUUID guid_type; | ||
226 | + uint32_t siglist_size; | ||
227 | + uint32_t header_size; | ||
228 | + uint32_t sig_size; | ||
229 | +}; | ||
230 | + | ||
231 | +/* EFI_VARIABLE_AUTHENTICATION_2 */ | ||
232 | +struct variable_auth_2 { | ||
233 | + struct efi_time timestamp; | ||
234 | + | ||
235 | + /* WIN_CERTIFICATE_UEFI_GUID */ | ||
236 | + uint32_t hdr_length; | ||
237 | + uint16_t hdr_revision; | ||
238 | + uint16_t hdr_cert_type; | ||
239 | + QemuUUID guid_cert_type; | ||
240 | + uint8_t cert_data[]; | ||
241 | +}; | ||
199 | + | 242 | + |
200 | +#endif /* QEMU_UEFI_VAR_SERVICE_EDK2_H */ | 243 | +#endif /* QEMU_UEFI_VAR_SERVICE_EDK2_H */ |
201 | -- | 244 | -- |
202 | 2.41.0 | 245 | 2.47.1 | diff view generated by jsdifflib |
1 | Add state structs and function declarations for the uefi-vars device. | 1 | Add state structs and function declarations for the uefi-vars device. |
---|---|---|---|
2 | 2 | ||
3 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 3 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
4 | --- | 4 | --- |
5 | include/hw/uefi/var-service.h | 119 ++++++++++++++++++++++++++++++++++ | 5 | include/hw/uefi/var-service.h | 186 ++++++++++++++++++++++++++++++++++ |
6 | 1 file changed, 119 insertions(+) | 6 | 1 file changed, 186 insertions(+) |
7 | create mode 100644 include/hw/uefi/var-service.h | 7 | create mode 100644 include/hw/uefi/var-service.h |
8 | 8 | ||
9 | diff --git a/include/hw/uefi/var-service.h b/include/hw/uefi/var-service.h | 9 | diff --git a/include/hw/uefi/var-service.h b/include/hw/uefi/var-service.h |
10 | new file mode 100644 | 10 | new file mode 100644 |
11 | index XXXXXXX..XXXXXXX | 11 | index XXXXXXX..XXXXXXX |
... | ... | ||
29 | + | 29 | + |
30 | +typedef struct uefi_variable uefi_variable; | 30 | +typedef struct uefi_variable uefi_variable; |
31 | +typedef struct uefi_var_policy uefi_var_policy; | 31 | +typedef struct uefi_var_policy uefi_var_policy; |
32 | +typedef struct uefi_vars_state uefi_vars_state; | 32 | +typedef struct uefi_vars_state uefi_vars_state; |
33 | + | 33 | + |
34 | +typedef struct uefi_vars_cert uefi_vars_cert; | ||
35 | +typedef struct uefi_vars_hash uefi_vars_hash; | ||
36 | +typedef struct uefi_vars_siglist uefi_vars_siglist; | ||
37 | + | ||
34 | +struct uefi_variable { | 38 | +struct uefi_variable { |
35 | + QemuUUID guid; | 39 | + QemuUUID guid; |
36 | + uint16_t *name; | 40 | + uint16_t *name; |
37 | + uint32_t name_size; | 41 | + uint32_t name_size; |
38 | + uint32_t attributes; | 42 | + uint32_t attributes; |
39 | + void *data; | 43 | + void *data; |
40 | + uint32_t data_size; | 44 | + uint32_t data_size; |
45 | + efi_time time; | ||
46 | + void *digest; | ||
47 | + uint32_t digest_size; | ||
41 | + QTAILQ_ENTRY(uefi_variable) next; | 48 | + QTAILQ_ENTRY(uefi_variable) next; |
42 | +}; | 49 | +}; |
43 | + | 50 | + |
44 | +struct uefi_var_policy { | 51 | +struct uefi_var_policy { |
45 | + variable_policy_entry *entry; | 52 | + variable_policy_entry *entry; |
46 | + uint32_t entry_size; | 53 | + uint32_t entry_size; |
47 | + uint16_t *name; | 54 | + uint16_t *name; |
48 | + uint32_t name_size; | 55 | + uint32_t name_size; |
56 | + | ||
57 | + /* number of hashmarks (wildcard character) in name */ | ||
49 | + uint32_t hashmarks; | 58 | + uint32_t hashmarks; |
59 | + | ||
50 | + QTAILQ_ENTRY(uefi_var_policy) next; | 60 | + QTAILQ_ENTRY(uefi_var_policy) next; |
51 | +}; | 61 | +}; |
52 | + | 62 | + |
53 | +struct uefi_vars_state { | 63 | +struct uefi_vars_state { |
54 | + MemoryRegion mr; | 64 | + MemoryRegion mr; |
... | ... | ||
68 | + | 78 | + |
69 | + /* storage accounting */ | 79 | + /* storage accounting */ |
70 | + uint64_t max_storage; | 80 | + uint64_t max_storage; |
71 | + uint64_t used_storage; | 81 | + uint64_t used_storage; |
72 | + | 82 | + |
83 | + /* config options */ | ||
73 | + char *jsonfile; | 84 | + char *jsonfile; |
74 | + int jsonfd; | 85 | + int jsonfd; |
86 | + bool force_secure_boot; | ||
87 | + bool disable_custom_mode; | ||
88 | +}; | ||
89 | + | ||
90 | +struct uefi_vars_cert { | ||
91 | + QTAILQ_ENTRY(uefi_vars_cert) next; | ||
92 | + QemuUUID owner; | ||
93 | + uint64_t size; | ||
94 | + uint8_t data[]; | ||
95 | +}; | ||
96 | + | ||
97 | +struct uefi_vars_hash { | ||
98 | + QTAILQ_ENTRY(uefi_vars_hash) next; | ||
99 | + QemuUUID owner; | ||
100 | + uint8_t data[]; | ||
101 | +}; | ||
102 | + | ||
103 | +struct uefi_vars_siglist { | ||
104 | + QTAILQ_HEAD(, uefi_vars_cert) x509; | ||
105 | + QTAILQ_HEAD(, uefi_vars_hash) sha256; | ||
75 | +}; | 106 | +}; |
76 | + | 107 | + |
77 | +/* vars-service-guid.c */ | 108 | +/* vars-service-guid.c */ |
78 | +extern QemuUUID EfiGlobalVariable; | 109 | +extern const QemuUUID EfiGlobalVariable; |
79 | +extern QemuUUID EfiImageSecurityDatabase; | 110 | +extern const QemuUUID EfiImageSecurityDatabase; |
80 | +extern QemuUUID EfiCustomModeEnable; | 111 | +extern const QemuUUID EfiCustomModeEnable; |
81 | +extern QemuUUID EfiSecureBootEnableDisable; | 112 | +extern const QemuUUID EfiSecureBootEnableDisable; |
82 | +extern QemuUUID EfiSmmVariableProtocolGuid; | 113 | + |
83 | +extern QemuUUID VarCheckPolicyLibMmiHandlerGuid; | 114 | +extern const QemuUUID EfiCertSha256Guid; |
84 | +extern QemuUUID EfiEndOfDxeEventGroupGuid; | 115 | +extern const QemuUUID EfiCertSha384Guid; |
85 | +extern QemuUUID EfiEventReadyToBootGuid; | 116 | +extern const QemuUUID EfiCertSha512Guid; |
86 | +extern QemuUUID EfiEventExitBootServicesGuid; | 117 | +extern const QemuUUID EfiCertRsa2048Guid; |
87 | + | 118 | +extern const QemuUUID EfiCertX509Guid; |
88 | +/* vars-service-core.c */ | 119 | +extern const QemuUUID EfiCertTypePkcs7Guid; |
89 | +extern const VMStateDescription vmstate_uefi_vars; | 120 | + |
121 | +extern const QemuUUID EfiSmmVariableProtocolGuid; | ||
122 | +extern const QemuUUID VarCheckPolicyLibMmiHandlerGuid; | ||
123 | + | ||
124 | +extern const QemuUUID EfiEndOfDxeEventGroupGuid; | ||
125 | +extern const QemuUUID EfiEventReadyToBootGuid; | ||
126 | +extern const QemuUUID EfiEventExitBootServicesGuid; | ||
127 | + | ||
128 | +/* vars-service-utils.c */ | ||
129 | +gboolean uefi_str_is_valid(const uint16_t *str, size_t len, | ||
130 | + gboolean must_be_null_terminated); | ||
90 | +size_t uefi_strlen(const uint16_t *str, size_t len); | 131 | +size_t uefi_strlen(const uint16_t *str, size_t len); |
132 | +gboolean uefi_str_equal_ex(const uint16_t *a, size_t alen, | ||
133 | + const uint16_t *b, size_t blen, | ||
134 | + gboolean wildcards_in_a); | ||
91 | +gboolean uefi_str_equal(const uint16_t *a, size_t alen, | 135 | +gboolean uefi_str_equal(const uint16_t *a, size_t alen, |
92 | + const uint16_t *b, size_t blen); | 136 | + const uint16_t *b, size_t blen); |
93 | +char *uefi_ucs2_to_ascii(const uint16_t *ucs2, uint64_t ucs2_size); | 137 | +char *uefi_ucs2_to_ascii(const uint16_t *ucs2, uint64_t ucs2_size); |
138 | +int uefi_time_compare(efi_time *a, efi_time *b); | ||
94 | +void uefi_trace_variable(const char *action, QemuUUID guid, | 139 | +void uefi_trace_variable(const char *action, QemuUUID guid, |
95 | + const uint16_t *name, uint64_t name_size); | 140 | + const uint16_t *name, uint64_t name_size); |
96 | +void uefi_trace_status(const char *action, efi_status status); | 141 | +void uefi_trace_status(const char *action, efi_status status); |
142 | + | ||
143 | +/* vars-service-core.c */ | ||
144 | +extern const VMStateDescription vmstate_uefi_vars; | ||
97 | +void uefi_vars_init(Object *obj, uefi_vars_state *uv); | 145 | +void uefi_vars_init(Object *obj, uefi_vars_state *uv); |
98 | +void uefi_vars_realize(uefi_vars_state *uv, Error **errp); | 146 | +void uefi_vars_realize(uefi_vars_state *uv, Error **errp); |
99 | +void uefi_vars_hard_reset(uefi_vars_state *uv); | 147 | +void uefi_vars_hard_reset(uefi_vars_state *uv); |
100 | + | 148 | + |
101 | +/* vars-service-json.c */ | 149 | +/* vars-service-json.c */ |
... | ... | ||
116 | +void uefi_vars_clear_all(uefi_vars_state *uv); | 164 | +void uefi_vars_clear_all(uefi_vars_state *uv); |
117 | +void uefi_vars_update_storage(uefi_vars_state *uv); | 165 | +void uefi_vars_update_storage(uefi_vars_state *uv); |
118 | +uint32_t uefi_vars_mm_vars_proto(uefi_vars_state *uv); | 166 | +uint32_t uefi_vars_mm_vars_proto(uefi_vars_state *uv); |
119 | + | 167 | + |
120 | +/* vars-service-auth.c */ | 168 | +/* vars-service-auth.c */ |
169 | +bool uefi_vars_is_sb_pk(uefi_variable *var); | ||
170 | +bool uefi_vars_is_sb_any(uefi_variable *var); | ||
171 | +efi_status uefi_vars_check_auth_2(uefi_vars_state *uv, uefi_variable *var, | ||
172 | + mm_variable_access *va, void *data); | ||
173 | +efi_status uefi_vars_check_secure_boot(uefi_vars_state *uv, uefi_variable *var); | ||
121 | +void uefi_vars_auth_init(uefi_vars_state *uv); | 174 | +void uefi_vars_auth_init(uefi_vars_state *uv); |
175 | + | ||
176 | +/* vars-service-pkcs7.c */ | ||
177 | +efi_status uefi_vars_check_pkcs7_2(uefi_variable *siglist, | ||
178 | + void **digest, uint32_t *digest_size, | ||
179 | + mm_variable_access *va, void *data); | ||
180 | + | ||
181 | +/* vars-service-siglist.c */ | ||
182 | +void uefi_vars_siglist_init(uefi_vars_siglist *siglist); | ||
183 | +void uefi_vars_siglist_free(uefi_vars_siglist *siglist); | ||
184 | +void uefi_vars_siglist_parse(uefi_vars_siglist *siglist, | ||
185 | + void *data, uint64_t size); | ||
186 | +uint64_t uefi_vars_siglist_blob_size(uefi_vars_siglist *siglist); | ||
187 | +void uefi_vars_siglist_blob_generate(uefi_vars_siglist *siglist, | ||
188 | + void *data, uint64_t size); | ||
122 | + | 189 | + |
123 | +/* vars-service-policy.c */ | 190 | +/* vars-service-policy.c */ |
124 | +extern const VMStateDescription vmstate_uefi_var_policy; | 191 | +extern const VMStateDescription vmstate_uefi_var_policy; |
125 | +efi_status uefi_vars_policy_check(uefi_vars_state *uv, | 192 | +efi_status uefi_vars_policy_check(uefi_vars_state *uv, |
126 | + uefi_variable *var, | 193 | + uefi_variable *var, |
... | ... | ||
130 | + variable_policy_entry *pe); | 197 | + variable_policy_entry *pe); |
131 | +uint32_t uefi_vars_mm_check_policy_proto(uefi_vars_state *uv); | 198 | +uint32_t uefi_vars_mm_check_policy_proto(uefi_vars_state *uv); |
132 | + | 199 | + |
133 | +#endif /* QEMU_UEFI_VAR_SERVICE_H */ | 200 | +#endif /* QEMU_UEFI_VAR_SERVICE_H */ |
134 | -- | 201 | -- |
135 | 2.41.0 | 202 | 2.47.1 | diff view generated by jsdifflib |
1 | Add variables for a bunch of GUIDs we will need. | 1 | Add variables for a bunch of UEFI GUIDs we will need. |
---|---|---|---|
2 | 2 | ||
3 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 3 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
4 | --- | 4 | --- |
5 | hw/uefi/var-service-guid.c | 61 ++++++++++++++++++++++++++++++++++++++ | 5 | hw/uefi/var-service-guid.c | 99 ++++++++++++++++++++++++++++++++++++++ |
6 | 1 file changed, 61 insertions(+) | 6 | 1 file changed, 99 insertions(+) |
7 | create mode 100644 hw/uefi/var-service-guid.c | 7 | create mode 100644 hw/uefi/var-service-guid.c |
8 | 8 | ||
9 | diff --git a/hw/uefi/var-service-guid.c b/hw/uefi/var-service-guid.c | 9 | diff --git a/hw/uefi/var-service-guid.c b/hw/uefi/var-service-guid.c |
10 | new file mode 100644 | 10 | new file mode 100644 |
11 | index XXXXXXX..XXXXXXX | 11 | index XXXXXXX..XXXXXXX |
... | ... | ||
17 | + * | 17 | + * |
18 | + * uefi vars device - GUIDs | 18 | + * uefi vars device - GUIDs |
19 | + */ | 19 | + */ |
20 | + | 20 | + |
21 | +#include "qemu/osdep.h" | 21 | +#include "qemu/osdep.h" |
22 | +#include "sysemu/dma.h" | 22 | +#include "system/dma.h" |
23 | + | 23 | + |
24 | +#include "hw/uefi/var-service.h" | 24 | +#include "hw/uefi/var-service.h" |
25 | + | 25 | + |
26 | +/* variable namespaces */ | 26 | +/* variable namespaces */ |
27 | + | 27 | + |
28 | +QemuUUID EfiGlobalVariable = { | 28 | +const QemuUUID EfiGlobalVariable = { |
29 | + .data = UUID_LE(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, | 29 | + .data = UUID_LE(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, |
30 | + 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c) | 30 | + 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c) |
31 | +}; | 31 | +}; |
32 | + | 32 | + |
33 | +QemuUUID EfiImageSecurityDatabase = { | 33 | +const QemuUUID EfiImageSecurityDatabase = { |
34 | + .data = UUID_LE(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, | 34 | + .data = UUID_LE(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, |
35 | + 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) | 35 | + 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f) |
36 | +}; | 36 | +}; |
37 | + | 37 | + |
38 | +QemuUUID EfiCustomModeEnable = { | 38 | +const QemuUUID EfiCustomModeEnable = { |
39 | + .data = UUID_LE(0xc076ec0c, 0x7028, 0x4399, 0xa0, 0x72, | 39 | + .data = UUID_LE(0xc076ec0c, 0x7028, 0x4399, 0xa0, 0x72, |
40 | + 0x71, 0xee, 0x5c, 0x44, 0x8b, 0x9f) | 40 | + 0x71, 0xee, 0x5c, 0x44, 0x8b, 0x9f) |
41 | +}; | 41 | +}; |
42 | + | 42 | + |
43 | +QemuUUID EfiSecureBootEnableDisable = { | 43 | +const QemuUUID EfiSecureBootEnableDisable = { |
44 | + .data = UUID_LE(0xf0a30bc7, 0xaf08, 0x4556, 0x99, 0xc4, | 44 | + .data = UUID_LE(0xf0a30bc7, 0xaf08, 0x4556, 0x99, 0xc4, |
45 | + 0x0, 0x10, 0x9, 0xc9, 0x3a, 0x44) | 45 | + 0x0, 0x10, 0x9, 0xc9, 0x3a, 0x44) |
46 | +}; | 46 | +}; |
47 | + | 47 | + |
48 | +/* protocols */ | 48 | +/* signatures */ |
49 | + | 49 | + |
50 | +QemuUUID EfiSmmVariableProtocolGuid = { | 50 | +const QemuUUID EfiCertSha256Guid = { |
51 | + .data = UUID_LE(0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, | ||
52 | + 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28) | ||
53 | +}; | ||
54 | + | ||
55 | +const QemuUUID EfiCertSha384Guid = { | ||
56 | + .data = UUID_LE(0xff3e5307, 0x9fd0, 0x48c9, 0x85, 0xf1, | ||
57 | + 0x8a, 0xd5, 0x6c, 0x70, 0x1e, 0x1) | ||
58 | +}; | ||
59 | + | ||
60 | +const QemuUUID EfiCertSha512Guid = { | ||
61 | + .data = UUID_LE(0x93e0fae, 0xa6c4, 0x4f50, 0x9f, 0x1b, | ||
62 | + 0xd4, 0x1e, 0x2b, 0x89, 0xc1, 0x9a) | ||
63 | +}; | ||
64 | + | ||
65 | +const QemuUUID EfiCertRsa2048Guid = { | ||
66 | + .data = UUID_LE(0x3c5766e8, 0x269c, 0x4e34, 0xaa, 0x14, | ||
67 | + 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6) | ||
68 | +}; | ||
69 | + | ||
70 | +const QemuUUID EfiCertX509Guid = { | ||
71 | + .data = UUID_LE(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, | ||
72 | + 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72) | ||
73 | +}; | ||
74 | + | ||
75 | +const QemuUUID EfiCertTypePkcs7Guid = { | ||
76 | + .data = UUID_LE(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, | ||
77 | + 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7) | ||
78 | +}; | ||
79 | + | ||
80 | +/* | ||
81 | + * mm_header.guid values that the guest DXE/BDS phases use for | ||
82 | + * sending requests to management mode | ||
83 | + */ | ||
84 | + | ||
85 | +const QemuUUID EfiSmmVariableProtocolGuid = { | ||
51 | + .data = UUID_LE(0xed32d533, 0x99e6, 0x4209, 0x9c, 0xc0, | 86 | + .data = UUID_LE(0xed32d533, 0x99e6, 0x4209, 0x9c, 0xc0, |
52 | + 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7) | 87 | + 0x2d, 0x72, 0xcd, 0xd9, 0x98, 0xa7) |
53 | +}; | 88 | +}; |
54 | + | 89 | + |
55 | +QemuUUID VarCheckPolicyLibMmiHandlerGuid = { | 90 | +const QemuUUID VarCheckPolicyLibMmiHandlerGuid = { |
56 | + .data = UUID_LE(0xda1b0d11, 0xd1a7, 0x46c4, 0x9d, 0xc9, | 91 | + .data = UUID_LE(0xda1b0d11, 0xd1a7, 0x46c4, 0x9d, 0xc9, |
57 | + 0xf3, 0x71, 0x48, 0x75, 0xc6, 0xeb) | 92 | + 0xf3, 0x71, 0x48, 0x75, 0xc6, 0xeb) |
58 | +}; | 93 | +}; |
59 | + | 94 | + |
60 | +/* events */ | 95 | +/* |
96 | + * mm_header.guid values that the guest DXE/BDS phases use for | ||
97 | + * reporting event groups being signaled to management mode | ||
98 | + */ | ||
61 | + | 99 | + |
62 | +QemuUUID EfiEndOfDxeEventGroupGuid = { | 100 | +const QemuUUID EfiEndOfDxeEventGroupGuid = { |
63 | + .data = UUID_LE(0x02CE967A, 0xDD7E, 0x4FFC, 0x9E, 0xE7, | 101 | + .data = UUID_LE(0x02ce967a, 0xdd7e, 0x4FFc, 0x9e, 0xe7, |
64 | + 0x81, 0x0C, 0xF0, 0x47, 0x08, 0x80) | 102 | + 0x81, 0x0c, 0xF0, 0x47, 0x08, 0x80) |
65 | +}; | 103 | +}; |
66 | + | 104 | + |
67 | +QemuUUID EfiEventReadyToBootGuid = { | 105 | +const QemuUUID EfiEventReadyToBootGuid = { |
68 | + .data = UUID_LE(0x7CE88FB3, 0x4BD7, 0x4679, 0x87, 0xA8, | 106 | + .data = UUID_LE(0x7ce88Fb3, 0x4bd7, 0x4679, 0x87, 0xa8, |
69 | + 0xA8, 0xD8, 0xDE, 0xE5, 0x0D, 0x2B) | 107 | + 0xa8, 0xd8, 0xde, 0xe5, 0x0d, 0x2b) |
70 | +}; | 108 | +}; |
71 | + | 109 | + |
72 | +QemuUUID EfiEventExitBootServicesGuid = { | 110 | +const QemuUUID EfiEventExitBootServicesGuid = { |
73 | + .data = UUID_LE(0x27ABF055, 0xB1B8, 0x4C26, 0x80, 0x48, | 111 | + .data = UUID_LE(0x27abF055, 0xb1b8, 0x4c26, 0x80, 0x48, |
74 | + 0x74, 0x8F, 0x37, 0xBA, 0xA2, 0xDF) | 112 | + 0x74, 0x8F, 0x37, 0xba, 0xa2, 0xdF) |
75 | +}; | 113 | +}; |
76 | -- | 114 | -- |
77 | 2.41.0 | 115 | 2.47.1 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | Add utility functions. Helpers for UEFI (ucs2) string handling. | ||
2 | Helpers for readable trace messages. Compare UEFI time stamps. | ||
1 | 3 | ||
4 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | ||
5 | --- | ||
6 | hw/uefi/var-service-utils.c | 241 ++++++++++++++++++++++++++++++++++++ | ||
7 | 1 file changed, 241 insertions(+) | ||
8 | create mode 100644 hw/uefi/var-service-utils.c | ||
9 | |||
10 | diff --git a/hw/uefi/var-service-utils.c b/hw/uefi/var-service-utils.c | ||
11 | new file mode 100644 | ||
12 | index XXXXXXX..XXXXXXX | ||
13 | --- /dev/null | ||
14 | +++ b/hw/uefi/var-service-utils.c | ||
15 | @@ -XXX,XX +XXX,XX @@ | ||
16 | +/* | ||
17 | + * SPDX-License-Identifier: GPL-2.0-or-later | ||
18 | + * | ||
19 | + * uefi vars device - helper functions for ucs2 strings and tracing | ||
20 | + */ | ||
21 | +#include "qemu/osdep.h" | ||
22 | +#include "system/dma.h" | ||
23 | + | ||
24 | +#include "hw/uefi/var-service.h" | ||
25 | + | ||
26 | +#include "trace/trace-hw_uefi.h" | ||
27 | + | ||
28 | +/* ------------------------------------------------------------------ */ | ||
29 | + | ||
30 | +/* | ||
31 | + * string helper functions. | ||
32 | + * | ||
33 | + * Most of the time uefi ucs2 strings are NULL-terminated, except | ||
34 | + * sometimes when they are not (for example in variable policies). | ||
35 | + */ | ||
36 | + | ||
37 | +gboolean uefi_str_is_valid(const uint16_t *str, size_t len, | ||
38 | + gboolean must_be_null_terminated) | ||
39 | +{ | ||
40 | + size_t pos = 0; | ||
41 | + | ||
42 | + for (;;) { | ||
43 | + if (pos == len) { | ||
44 | + if (must_be_null_terminated) { | ||
45 | + return false; | ||
46 | + } else { | ||
47 | + return true; | ||
48 | + } | ||
49 | + } | ||
50 | + switch (str[pos]) { | ||
51 | + case 0: | ||
52 | + /* end of string */ | ||
53 | + return true; | ||
54 | + case 0xd800 ... 0xdfff: | ||
55 | + /* reject surrogates */ | ||
56 | + return false; | ||
57 | + default: | ||
58 | + /* char is good, check next */ | ||
59 | + break; | ||
60 | + } | ||
61 | + pos++; | ||
62 | + } | ||
63 | +} | ||
64 | + | ||
65 | +size_t uefi_strlen(const uint16_t *str, size_t len) | ||
66 | +{ | ||
67 | + size_t pos = 0; | ||
68 | + | ||
69 | + for (;;) { | ||
70 | + if (pos == len) { | ||
71 | + return pos; | ||
72 | + } | ||
73 | + if (str[pos] == 0) { | ||
74 | + return pos; | ||
75 | + } | ||
76 | + pos++; | ||
77 | + } | ||
78 | +} | ||
79 | + | ||
80 | +gboolean uefi_str_equal_ex(const uint16_t *a, size_t alen, | ||
81 | + const uint16_t *b, size_t blen, | ||
82 | + gboolean wildcards_in_a) | ||
83 | +{ | ||
84 | + size_t pos = 0; | ||
85 | + | ||
86 | + alen = alen / 2; | ||
87 | + blen = blen / 2; | ||
88 | + for (;;) { | ||
89 | + if (pos == alen && pos == blen) { | ||
90 | + return true; | ||
91 | + } | ||
92 | + if (pos == alen && b[pos] == 0) { | ||
93 | + return true; | ||
94 | + } | ||
95 | + if (pos == blen && a[pos] == 0) { | ||
96 | + return true; | ||
97 | + } | ||
98 | + if (pos == alen || pos == blen) { | ||
99 | + return false; | ||
100 | + } | ||
101 | + if (a[pos] == 0 && b[pos] == 0) { | ||
102 | + return true; | ||
103 | + } | ||
104 | + | ||
105 | + if (wildcards_in_a && a[pos] == '#') { | ||
106 | + if (!isxdigit(b[pos])) { | ||
107 | + return false; | ||
108 | + } | ||
109 | + } else { | ||
110 | + if (a[pos] != b[pos]) { | ||
111 | + return false; | ||
112 | + } | ||
113 | + } | ||
114 | + pos++; | ||
115 | + } | ||
116 | +} | ||
117 | + | ||
118 | +gboolean uefi_str_equal(const uint16_t *a, size_t alen, | ||
119 | + const uint16_t *b, size_t blen) | ||
120 | +{ | ||
121 | + return uefi_str_equal_ex(a, alen, b, blen, false); | ||
122 | +} | ||
123 | + | ||
124 | +char *uefi_ucs2_to_ascii(const uint16_t *ucs2, uint64_t ucs2_size) | ||
125 | +{ | ||
126 | + char *str = g_malloc0(ucs2_size / 2 + 1); | ||
127 | + int i; | ||
128 | + | ||
129 | + for (i = 0; i * 2 < ucs2_size; i++) { | ||
130 | + if (ucs2[i] == 0) { | ||
131 | + break; | ||
132 | + } | ||
133 | + if (ucs2[i] < 128) { | ||
134 | + str[i] = ucs2[i]; | ||
135 | + } else { | ||
136 | + str[i] = '?'; | ||
137 | + } | ||
138 | + } | ||
139 | + str[i] = 0; | ||
140 | + return str; | ||
141 | +} | ||
142 | + | ||
143 | +/* ------------------------------------------------------------------ */ | ||
144 | +/* time helper functions */ | ||
145 | + | ||
146 | +int uefi_time_compare(efi_time *a, efi_time *b) | ||
147 | +{ | ||
148 | + if (a->year < b->year) { | ||
149 | + return -1; | ||
150 | + } | ||
151 | + if (a->year > b->year) { | ||
152 | + return 1; | ||
153 | + } | ||
154 | + | ||
155 | + if (a->month < b->month) { | ||
156 | + return -1; | ||
157 | + } | ||
158 | + if (a->month > b->month) { | ||
159 | + return 1; | ||
160 | + } | ||
161 | + | ||
162 | + if (a->day < b->day) { | ||
163 | + return -1; | ||
164 | + } | ||
165 | + if (a->day > b->day) { | ||
166 | + return 1; | ||
167 | + } | ||
168 | + | ||
169 | + if (a->hour < b->hour) { | ||
170 | + return -1; | ||
171 | + } | ||
172 | + if (a->hour > b->hour) { | ||
173 | + return 1; | ||
174 | + } | ||
175 | + | ||
176 | + if (a->minute < b->minute) { | ||
177 | + return -1; | ||
178 | + } | ||
179 | + if (a->minute > b->minute) { | ||
180 | + return 1; | ||
181 | + } | ||
182 | + | ||
183 | + if (a->second < b->second) { | ||
184 | + return -1; | ||
185 | + } | ||
186 | + if (a->second > b->second) { | ||
187 | + return 1; | ||
188 | + } | ||
189 | + | ||
190 | + if (a->nanosecond < b->nanosecond) { | ||
191 | + return -1; | ||
192 | + } | ||
193 | + if (a->nanosecond > b->nanosecond) { | ||
194 | + return 1; | ||
195 | + } | ||
196 | + | ||
197 | + return 0; | ||
198 | +} | ||
199 | + | ||
200 | +/* ------------------------------------------------------------------ */ | ||
201 | +/* tracing helper functions */ | ||
202 | + | ||
203 | +void uefi_trace_variable(const char *action, QemuUUID guid, | ||
204 | + const uint16_t *name, uint64_t name_size) | ||
205 | +{ | ||
206 | + QemuUUID be = qemu_uuid_bswap(guid); | ||
207 | + char *str_uuid = qemu_uuid_unparse_strdup(&be); | ||
208 | + char *str_name = uefi_ucs2_to_ascii(name, name_size); | ||
209 | + | ||
210 | + trace_uefi_variable(action, str_name, name_size, str_uuid); | ||
211 | + | ||
212 | + g_free(str_name); | ||
213 | + g_free(str_uuid); | ||
214 | +} | ||
215 | + | ||
216 | +void uefi_trace_status(const char *action, efi_status status) | ||
217 | +{ | ||
218 | + switch (status) { | ||
219 | + case EFI_SUCCESS: | ||
220 | + trace_uefi_status(action, "success"); | ||
221 | + break; | ||
222 | + case EFI_INVALID_PARAMETER: | ||
223 | + trace_uefi_status(action, "invalid parameter"); | ||
224 | + break; | ||
225 | + case EFI_UNSUPPORTED: | ||
226 | + trace_uefi_status(action, "unsupported"); | ||
227 | + break; | ||
228 | + case EFI_BAD_BUFFER_SIZE: | ||
229 | + trace_uefi_status(action, "bad buffer size"); | ||
230 | + break; | ||
231 | + case EFI_BUFFER_TOO_SMALL: | ||
232 | + trace_uefi_status(action, "buffer too small"); | ||
233 | + break; | ||
234 | + case EFI_WRITE_PROTECTED: | ||
235 | + trace_uefi_status(action, "write protected"); | ||
236 | + break; | ||
237 | + case EFI_OUT_OF_RESOURCES: | ||
238 | + trace_uefi_status(action, "out of resources"); | ||
239 | + break; | ||
240 | + case EFI_NOT_FOUND: | ||
241 | + trace_uefi_status(action, "not found"); | ||
242 | + break; | ||
243 | + case EFI_ACCESS_DENIED: | ||
244 | + trace_uefi_status(action, "access denied"); | ||
245 | + break; | ||
246 | + case EFI_ALREADY_STARTED: | ||
247 | + trace_uefi_status(action, "already started"); | ||
248 | + break; | ||
249 | + case EFI_SECURITY_VIOLATION: | ||
250 | + trace_uefi_status(action, "security violation"); | ||
251 | + break; | ||
252 | + default: | ||
253 | + trace_uefi_status(action, "unknown error"); | ||
254 | + break; | ||
255 | + } | ||
256 | +} | ||
257 | -- | ||
258 | 2.47.1 | diff view generated by jsdifflib |
1 | This is the uefi variable service (EfiSmmVariableProtocol), | 1 | This is the uefi variable service (EfiSmmVariableProtocol), providing |
---|---|---|---|
2 | providing functions for reading and writing variables. | 2 | functions for listing, reading and updating variables. |
3 | 3 | ||
4 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 4 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
5 | --- | 5 | --- |
6 | hw/uefi/var-service-vars.c | 602 +++++++++++++++++++++++++++++++++++++ | 6 | hw/uefi/var-service-vars.c | 725 +++++++++++++++++++++++++++++++++++++ |
7 | 1 file changed, 602 insertions(+) | 7 | 1 file changed, 725 insertions(+) |
8 | create mode 100644 hw/uefi/var-service-vars.c | 8 | create mode 100644 hw/uefi/var-service-vars.c |
9 | 9 | ||
10 | diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c | 10 | diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c |
11 | new file mode 100644 | 11 | new file mode 100644 |
12 | index XXXXXXX..XXXXXXX | 12 | index XXXXXXX..XXXXXXX |
... | ... | ||
17 | + * SPDX-License-Identifier: GPL-2.0-or-later | 17 | + * SPDX-License-Identifier: GPL-2.0-or-later |
18 | + * | 18 | + * |
19 | + * uefi vars device - EfiSmmVariableProtocol implementation | 19 | + * uefi vars device - EfiSmmVariableProtocol implementation |
20 | + */ | 20 | + */ |
21 | +#include "qemu/osdep.h" | 21 | +#include "qemu/osdep.h" |
22 | +#include "sysemu/dma.h" | 22 | +#include "qemu/error-report.h" |
23 | +#include "system/dma.h" | ||
23 | +#include "migration/vmstate.h" | 24 | +#include "migration/vmstate.h" |
24 | + | 25 | + |
25 | +#include "hw/uefi/var-service.h" | 26 | +#include "hw/uefi/var-service.h" |
26 | +#include "hw/uefi/var-service-api.h" | 27 | +#include "hw/uefi/var-service-api.h" |
27 | +#include "hw/uefi/var-service-edk2.h" | 28 | +#include "hw/uefi/var-service-edk2.h" |
28 | + | 29 | + |
29 | +#include "trace/trace-hw_uefi.h" | 30 | +#include "trace/trace-hw_uefi.h" |
31 | + | ||
32 | +#define EFI_VARIABLE_ATTRIBUTE_SUPPORTED \ | ||
33 | + (EFI_VARIABLE_NON_VOLATILE | \ | ||
34 | + EFI_VARIABLE_BOOTSERVICE_ACCESS | \ | ||
35 | + EFI_VARIABLE_RUNTIME_ACCESS | \ | ||
36 | + EFI_VARIABLE_HARDWARE_ERROR_RECORD | \ | ||
37 | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | \ | ||
38 | + EFI_VARIABLE_APPEND_WRITE) | ||
39 | + | ||
40 | + | ||
41 | +const VMStateDescription vmstate_uefi_time = { | ||
42 | + .name = "uefi-time", | ||
43 | + .fields = (VMStateField[]) { | ||
44 | + VMSTATE_UINT16(year, efi_time), | ||
45 | + VMSTATE_UINT8(month, efi_time), | ||
46 | + VMSTATE_UINT8(day, efi_time), | ||
47 | + VMSTATE_UINT8(hour, efi_time), | ||
48 | + VMSTATE_UINT8(minute, efi_time), | ||
49 | + VMSTATE_UINT8(second, efi_time), | ||
50 | + VMSTATE_UINT32(nanosecond, efi_time), | ||
51 | + VMSTATE_END_OF_LIST() | ||
52 | + }, | ||
53 | +}; | ||
30 | + | 54 | + |
31 | +const VMStateDescription vmstate_uefi_variable = { | 55 | +const VMStateDescription vmstate_uefi_variable = { |
32 | + .name = "uefi-variable", | 56 | + .name = "uefi-variable", |
33 | + .fields = (VMStateField[]) { | 57 | + .fields = (VMStateField[]) { |
34 | + VMSTATE_UINT8_ARRAY_V(guid.data, uefi_variable, sizeof(QemuUUID), 0), | 58 | + VMSTATE_UINT8_ARRAY_V(guid.data, uefi_variable, sizeof(QemuUUID), 0), |
35 | + VMSTATE_UINT32(name_size, uefi_variable), | 59 | + VMSTATE_UINT32(name_size, uefi_variable), |
36 | + VMSTATE_UINT32(data_size, uefi_variable), | 60 | + VMSTATE_UINT32(data_size, uefi_variable), |
37 | + VMSTATE_UINT32(attributes, uefi_variable), | 61 | + VMSTATE_UINT32(attributes, uefi_variable), |
38 | + VMSTATE_VBUFFER_ALLOC_UINT32(name, uefi_variable, 0, NULL, name_size), | 62 | + VMSTATE_VBUFFER_ALLOC_UINT32(name, uefi_variable, 0, NULL, name_size), |
39 | + VMSTATE_VBUFFER_ALLOC_UINT32(data, uefi_variable, 0, NULL, data_size), | 63 | + VMSTATE_VBUFFER_ALLOC_UINT32(data, uefi_variable, 0, NULL, data_size), |
64 | + VMSTATE_STRUCT(time, uefi_variable, 0, vmstate_uefi_time, efi_time), | ||
40 | + VMSTATE_END_OF_LIST() | 65 | + VMSTATE_END_OF_LIST() |
41 | + }, | 66 | + }, |
42 | +}; | 67 | +}; |
43 | + | 68 | + |
44 | +uefi_variable *uefi_vars_find_variable(uefi_vars_state *uv, QemuUUID guid, | 69 | +uefi_variable *uefi_vars_find_variable(uefi_vars_state *uv, QemuUUID guid, |
... | ... | ||
50 | + if (!uefi_str_equal(var->name, var->name_size, | 75 | + if (!uefi_str_equal(var->name, var->name_size, |
51 | + name, name_size)) { | 76 | + name, name_size)) { |
52 | + continue; | 77 | + continue; |
53 | + } | 78 | + } |
54 | + if (!qemu_uuid_is_equal(&var->guid, &guid)) { | 79 | + if (!qemu_uuid_is_equal(&var->guid, &guid)) { |
80 | + continue; | ||
81 | + } | ||
82 | + if (!var->data_size) { | ||
83 | + /* in process of being created/updated */ | ||
55 | + continue; | 84 | + continue; |
56 | + } | 85 | + } |
57 | + return var; | 86 | + return var; |
58 | + } | 87 | + } |
59 | + return NULL; | 88 | + return NULL; |
... | ... | ||
70 | + var->name = g_malloc(name_size); | 99 | + var->name = g_malloc(name_size); |
71 | + memcpy(var->name, name, name_size); | 100 | + memcpy(var->name, name, name_size); |
72 | + var->name_size = name_size; | 101 | + var->name_size = name_size; |
73 | + var->attributes = attributes; | 102 | + var->attributes = attributes; |
74 | + | 103 | + |
104 | + var->attributes &= ~EFI_VARIABLE_APPEND_WRITE; | ||
105 | + | ||
75 | + QTAILQ_INSERT_TAIL(&uv->variables, var, next); | 106 | + QTAILQ_INSERT_TAIL(&uv->variables, var, next); |
76 | + return var; | 107 | + return var; |
77 | +} | 108 | +} |
78 | + | 109 | + |
79 | +static void del_variable(uefi_vars_state *uv, uefi_variable *var) | 110 | +static void del_variable(uefi_vars_state *uv, uefi_variable *var) |
... | ... | ||
83 | + } | 114 | + } |
84 | + | 115 | + |
85 | + QTAILQ_REMOVE(&uv->variables, var, next); | 116 | + QTAILQ_REMOVE(&uv->variables, var, next); |
86 | + g_free(var->data); | 117 | + g_free(var->data); |
87 | + g_free(var->name); | 118 | + g_free(var->name); |
119 | + g_free(var->digest); | ||
88 | + g_free(var); | 120 | + g_free(var); |
89 | +} | 121 | +} |
90 | + | 122 | + |
91 | +static size_t variable_size(uefi_variable *var) | 123 | +static size_t variable_size(uefi_variable *var) |
92 | +{ | 124 | +{ |
93 | + size_t size; | 125 | + size_t size; |
94 | + | 126 | + |
95 | + size = sizeof(*var); | 127 | + size = sizeof(*var); |
96 | + size += var->name_size; | 128 | + size += var->name_size; |
97 | + size += var->data_size; | 129 | + size += var->data_size; |
130 | + size += var->digest_size; | ||
98 | + return size; | 131 | + return size; |
99 | +} | 132 | +} |
100 | + | 133 | + |
101 | +void uefi_vars_set_variable(uefi_vars_state *uv, QemuUUID guid, | 134 | +void uefi_vars_set_variable(uefi_vars_state *uv, QemuUUID guid, |
102 | + const uint16_t *name, uint64_t name_size, | 135 | + const uint16_t *name, uint64_t name_size, |
... | ... | ||
151 | + QTAILQ_FOREACH(var, &uv->variables, next) { | 184 | + QTAILQ_FOREACH(var, &uv->variables, next) { |
152 | + uv->used_storage += variable_size(var); | 185 | + uv->used_storage += variable_size(var); |
153 | + } | 186 | + } |
154 | +} | 187 | +} |
155 | + | 188 | + |
156 | +static efi_status check_secure_boot(uefi_vars_state *uv, uefi_variable *var) | ||
157 | +{ | ||
158 | + static const uint16_t pk[] = { 'P', 'K', 0 }; | ||
159 | + static const uint16_t kek[] = { 'K', 'E', 'K', 0 }; | ||
160 | + static const uint16_t db[] = { 'd', 'b', 0 }; | ||
161 | + static const uint16_t dbx[] = { 'd', 'b', 'x', 0 }; | ||
162 | + | ||
163 | + /* TODO (reject for now) */ | ||
164 | + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && | ||
165 | + uefi_str_equal(var->name, var->name_size, pk, sizeof(pk))) { | ||
166 | + return EFI_WRITE_PROTECTED; | ||
167 | + } | ||
168 | + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && | ||
169 | + uefi_str_equal(var->name, var->name_size, kek, sizeof(kek))) { | ||
170 | + return EFI_WRITE_PROTECTED; | ||
171 | + } | ||
172 | + | ||
173 | + if (qemu_uuid_is_equal(&var->guid, &EfiImageSecurityDatabase) && | ||
174 | + uefi_str_equal(var->name, var->name_size, db, sizeof(db))) { | ||
175 | + return EFI_WRITE_PROTECTED; | ||
176 | + } | ||
177 | + if (qemu_uuid_is_equal(&var->guid, &EfiImageSecurityDatabase) && | ||
178 | + uefi_str_equal(var->name, var->name_size, dbx, sizeof(dbx))) { | ||
179 | + return EFI_WRITE_PROTECTED; | ||
180 | + } | ||
181 | + | ||
182 | + return EFI_SUCCESS; | ||
183 | +} | ||
184 | + | ||
185 | +static gboolean check_access(uefi_vars_state *uv, uefi_variable *var) | 189 | +static gboolean check_access(uefi_vars_state *uv, uefi_variable *var) |
186 | +{ | 190 | +{ |
187 | + if (!uv->exit_boot_service) { | 191 | + if (!uv->exit_boot_service) { |
188 | + if (!(var->attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)) { | 192 | + if (!(var->attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)) { |
189 | + return false; | 193 | + return false; |
... | ... | ||
203 | + | 207 | + |
204 | + if (old_var) { | 208 | + if (old_var) { |
205 | + if (!check_access(uv, old_var)) { | 209 | + if (!check_access(uv, old_var)) { |
206 | + return EFI_ACCESS_DENIED; | 210 | + return EFI_ACCESS_DENIED; |
207 | + } | 211 | + } |
208 | + if (old_var->attributes & | ||
209 | + (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | | ||
210 | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { | ||
211 | + /* TODO (reject for now) */ | ||
212 | + return EFI_WRITE_PROTECTED; | ||
213 | + } | ||
214 | + } | 212 | + } |
215 | + | 213 | + |
216 | + if (new_var) { | 214 | + if (new_var) { |
215 | + if (new_var->attributes & ~EFI_VARIABLE_ATTRIBUTE_SUPPORTED) { | ||
216 | + return EFI_UNSUPPORTED; | ||
217 | + } | ||
217 | + if (!check_access(uv, new_var)) { | 218 | + if (!check_access(uv, new_var)) { |
218 | + return EFI_ACCESS_DENIED; | 219 | + return EFI_ACCESS_DENIED; |
219 | + } | 220 | + } |
220 | + } | 221 | + } |
221 | + | 222 | + |
... | ... | ||
226 | + } | 227 | + } |
227 | + | 228 | + |
228 | + if (new_var) { | 229 | + if (new_var) { |
229 | + /* create + update */ | 230 | + /* create + update */ |
230 | + status = uefi_vars_policy_check(uv, new_var, old_var == NULL); | 231 | + status = uefi_vars_policy_check(uv, new_var, old_var == NULL); |
231 | + } else if (old_var) { | 232 | + } else { |
232 | + /* delete */ | 233 | + /* delete */ |
234 | + g_assert(old_var); | ||
233 | + status = uefi_vars_policy_check(uv, old_var, false); | 235 | + status = uefi_vars_policy_check(uv, old_var, false); |
234 | + } | 236 | + } |
235 | + if (status != EFI_SUCCESS) { | 237 | + if (status != EFI_SUCCESS) { |
236 | + return status; | 238 | + return status; |
237 | + } | 239 | + } |
238 | + | 240 | + |
239 | + status = check_secure_boot(uv, new_var ?: old_var); | 241 | + status = uefi_vars_check_secure_boot(uv, new_var ?: old_var); |
240 | + if (status != EFI_SUCCESS) { | 242 | + if (status != EFI_SUCCESS) { |
241 | + return status; | 243 | + return status; |
242 | + } | 244 | + } |
243 | + | 245 | + |
244 | + return EFI_SUCCESS; | 246 | + return EFI_SUCCESS; |
247 | +} | ||
248 | + | ||
249 | +static void append_write(uefi_variable *old_var, | ||
250 | + uefi_variable *new_var) | ||
251 | +{ | ||
252 | + uefi_vars_siglist siglist; | ||
253 | + uint64_t size; | ||
254 | + void *data; | ||
255 | + | ||
256 | + uefi_vars_siglist_init(&siglist); | ||
257 | + uefi_vars_siglist_parse(&siglist, old_var->data, old_var->data_size); | ||
258 | + uefi_vars_siglist_parse(&siglist, new_var->data, new_var->data_size); | ||
259 | + | ||
260 | + size = uefi_vars_siglist_blob_size(&siglist); | ||
261 | + data = g_malloc(size); | ||
262 | + uefi_vars_siglist_blob_generate(&siglist, data, size); | ||
263 | + | ||
264 | + g_free(new_var->data); | ||
265 | + new_var->data = data; | ||
266 | + new_var->data_size = size; | ||
267 | + | ||
268 | + uefi_vars_siglist_free(&siglist); | ||
245 | +} | 269 | +} |
246 | + | 270 | + |
247 | +static size_t uefi_vars_mm_error(mm_header *mhdr, mm_variable *mvar, | 271 | +static size_t uefi_vars_mm_error(mm_header *mhdr, mm_variable *mvar, |
248 | + uint64_t status) | 272 | + uint64_t status) |
249 | +{ | 273 | +{ |
... | ... | ||
256 | +{ | 280 | +{ |
257 | + mm_variable_access *va = func; | 281 | + mm_variable_access *va = func; |
258 | + uint16_t *name; | 282 | + uint16_t *name; |
259 | + void *data; | 283 | + void *data; |
260 | + uefi_variable *var; | 284 | + uefi_variable *var; |
261 | + size_t length; | 285 | + uint64_t length; |
262 | + | 286 | + |
263 | + length = sizeof(*mvar) + sizeof(*va); | 287 | + length = sizeof(*mvar) + sizeof(*va); |
264 | + if (mhdr->length < length) { | 288 | + if (mhdr->length < length) { |
265 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 289 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
266 | + } | 290 | + } |
... | ... | ||
269 | + va->data_size > uv->max_storage) { | 293 | + va->data_size > uv->max_storage) { |
270 | + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); | 294 | + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); |
271 | + } | 295 | + } |
272 | + | 296 | + |
273 | + name = func + sizeof(*va); | 297 | + name = func + sizeof(*va); |
274 | + length += va->name_size; | 298 | + if (uadd64_overflow(length, va->name_size, &length)) { |
275 | + if (mhdr->length < length) { | 299 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
276 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 300 | + } |
301 | + if (mhdr->length < length) { | ||
302 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | ||
303 | + } | ||
304 | + | ||
305 | + if (!uefi_str_is_valid(name, va->name_size, true)) { | ||
306 | + return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER); | ||
277 | + } | 307 | + } |
278 | + | 308 | + |
279 | + uefi_trace_variable(__func__, va->guid, name, va->name_size); | 309 | + uefi_trace_variable(__func__, va->guid, name, va->name_size); |
280 | + | 310 | + |
281 | + var = uefi_vars_find_variable(uv, va->guid, name, va->name_size); | 311 | + var = uefi_vars_find_variable(uv, va->guid, name, va->name_size); |
... | ... | ||
287 | + if (!check_access(uv, var)) { | 317 | + if (!check_access(uv, var)) { |
288 | + return uefi_vars_mm_error(mhdr, mvar, EFI_ACCESS_DENIED); | 318 | + return uefi_vars_mm_error(mhdr, mvar, EFI_ACCESS_DENIED); |
289 | + } | 319 | + } |
290 | + | 320 | + |
291 | + data = func + sizeof(*va) + va->name_size; | 321 | + data = func + sizeof(*va) + va->name_size; |
292 | + length += var->data_size; | 322 | + if (uadd64_overflow(length, va->data_size, &length)) { |
323 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | ||
324 | + } | ||
293 | + if (uv->buf_size < length) { | 325 | + if (uv->buf_size < length) { |
294 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 326 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
295 | + } | 327 | + } |
296 | + | 328 | + |
297 | + va->attributes = var->attributes; | 329 | + va->attributes = var->attributes; |
298 | + va->data_size = var->data_size; | 330 | + if (va->data_size < var->data_size) { |
299 | + memcpy(data, var->data, var->data_size); | 331 | + va->data_size = var->data_size; |
300 | + mvar->status = EFI_SUCCESS; | 332 | + length -= va->data_size; |
333 | + mvar->status = EFI_BUFFER_TOO_SMALL; | ||
334 | + } else { | ||
335 | + va->data_size = var->data_size; | ||
336 | + memcpy(data, var->data, var->data_size); | ||
337 | + mvar->status = EFI_SUCCESS; | ||
338 | + } | ||
301 | + return length; | 339 | + return length; |
302 | +} | 340 | +} |
303 | + | 341 | + |
304 | +static size_t | 342 | +static size_t |
305 | +uefi_vars_mm_get_next_variable(uefi_vars_state *uv, mm_header *mhdr, | 343 | +uefi_vars_mm_get_next_variable(uefi_vars_state *uv, mm_header *mhdr, |
306 | + mm_variable *mvar, void *func) | 344 | + mm_variable *mvar, void *func) |
307 | +{ | 345 | +{ |
308 | + mm_next_variable *nv = func; | 346 | + mm_next_variable *nv = func; |
309 | + uefi_variable *var; | 347 | + uefi_variable *var; |
310 | + uint16_t *name; | 348 | + uint16_t *name; |
311 | + size_t length; | 349 | + uint64_t length; |
312 | + | 350 | + |
313 | + length = sizeof(*mvar) + sizeof(*nv); | 351 | + length = sizeof(*mvar) + sizeof(*nv); |
314 | + if (mhdr->length < length) { | 352 | + if (mhdr->length < length) { |
315 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 353 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
316 | + } | 354 | + } |
317 | + | 355 | + |
318 | + if (nv->name_size > uv->max_storage) { | 356 | + if (nv->name_size > uv->max_storage) { |
319 | + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); | 357 | + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); |
320 | + } | 358 | + } |
321 | + | 359 | + |
322 | + name = func + sizeof(*nv); | 360 | + name = func + sizeof(*nv); |
323 | + length += nv->name_size; | 361 | + if (uadd64_overflow(length, nv->name_size, &length)) { |
324 | + if (mhdr->length < length) { | 362 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
325 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 363 | + } |
364 | + if (mhdr->length < length) { | ||
365 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | ||
366 | + } | ||
367 | + | ||
368 | + if (!uefi_str_is_valid(name, nv->name_size, true)) { | ||
369 | + return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER); | ||
326 | + } | 370 | + } |
327 | + | 371 | + |
328 | + if (uefi_strlen(name, nv->name_size) == 0) { | 372 | + if (uefi_strlen(name, nv->name_size) == 0) { |
329 | + /* empty string -> first */ | 373 | + /* empty string -> first */ |
330 | + var = QTAILQ_FIRST(&uv->variables); | 374 | + var = QTAILQ_FIRST(&uv->variables); |
... | ... | ||
354 | + memcpy(name, var->name, var->name_size); | 398 | + memcpy(name, var->name, var->name_size); |
355 | + mvar->status = EFI_SUCCESS; | 399 | + mvar->status = EFI_SUCCESS; |
356 | + return length; | 400 | + return length; |
357 | +} | 401 | +} |
358 | + | 402 | + |
403 | +static bool uefi_vars_mm_digest_compare(uefi_variable *old_var, | ||
404 | + uefi_variable *new_var) | ||
405 | +{ | ||
406 | + if (!old_var->digest || | ||
407 | + !new_var->digest || | ||
408 | + !old_var->digest_size || | ||
409 | + !new_var->digest_size) { | ||
410 | + /* should not happen */ | ||
411 | + trace_uefi_vars_security_violation("inconsistent authvar digest state"); | ||
412 | + return false; | ||
413 | + } | ||
414 | + if (old_var->digest_size != new_var->digest_size) { | ||
415 | + trace_uefi_vars_security_violation("authvar digest size mismatch"); | ||
416 | + return false; | ||
417 | + } | ||
418 | + if (memcmp(old_var->digest, new_var->digest, | ||
419 | + old_var->digest_size) != 0) { | ||
420 | + trace_uefi_vars_security_violation("authvar digest data mismatch"); | ||
421 | + return false; | ||
422 | + } | ||
423 | + return true; | ||
424 | +} | ||
425 | + | ||
359 | +static size_t uefi_vars_mm_set_variable(uefi_vars_state *uv, mm_header *mhdr, | 426 | +static size_t uefi_vars_mm_set_variable(uefi_vars_state *uv, mm_header *mhdr, |
360 | + mm_variable *mvar, void *func) | 427 | + mm_variable *mvar, void *func) |
361 | +{ | 428 | +{ |
362 | + mm_variable_access *va = func; | 429 | + mm_variable_access *va = func; |
363 | + uint32_t attributes = 0; | 430 | + uint32_t attributes = 0; |
364 | + uint16_t *name; | 431 | + uint16_t *name; |
365 | + void *data; | 432 | + void *data; |
366 | + uefi_variable *old_var, *new_var; | 433 | + uefi_variable *old_var, *new_var; |
367 | + size_t length, new_storage; | 434 | + uint64_t length; |
435 | + size_t new_storage; | ||
368 | + efi_status status; | 436 | + efi_status status; |
369 | + | 437 | + |
370 | + length = sizeof(*mvar) + sizeof(*va); | 438 | + length = sizeof(*mvar) + sizeof(*va); |
371 | + if (mhdr->length < length) { | 439 | + if (mhdr->length < length) { |
372 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 440 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
... | ... | ||
376 | + va->data_size > uv->max_storage) { | 444 | + va->data_size > uv->max_storage) { |
377 | + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); | 445 | + return uefi_vars_mm_error(mhdr, mvar, EFI_OUT_OF_RESOURCES); |
378 | + } | 446 | + } |
379 | + | 447 | + |
380 | + name = func + sizeof(*va); | 448 | + name = func + sizeof(*va); |
381 | + length += va->name_size; | 449 | + if (uadd64_overflow(length, va->name_size, &length)) { |
450 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | ||
451 | + } | ||
382 | + if (mhdr->length < length) { | 452 | + if (mhdr->length < length) { |
383 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 453 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
384 | + } | 454 | + } |
385 | + | 455 | + |
386 | + data = func + sizeof(*va) + va->name_size; | 456 | + data = func + sizeof(*va) + va->name_size; |
387 | + length += va->data_size; | 457 | + if (uadd64_overflow(length, va->data_size, &length)) { |
458 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | ||
459 | + } | ||
388 | + if (mhdr->length < length) { | 460 | + if (mhdr->length < length) { |
389 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 461 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
390 | + } | 462 | + } |
391 | + | 463 | + |
392 | + g_assert(va->name_size < G_MAXUINT32); | 464 | + g_assert(va->name_size < G_MAXUINT32); |
393 | + g_assert(va->data_size < G_MAXUINT32); | 465 | + g_assert(va->data_size < G_MAXUINT32); |
466 | + | ||
467 | + if (!uefi_str_is_valid(name, va->name_size, true)) { | ||
468 | + return uefi_vars_mm_error(mhdr, mvar, EFI_INVALID_PARAMETER); | ||
469 | + } | ||
394 | + | 470 | + |
395 | + uefi_trace_variable(__func__, va->guid, name, va->name_size); | 471 | + uefi_trace_variable(__func__, va->guid, name, va->name_size); |
396 | + | 472 | + |
397 | + old_var = uefi_vars_find_variable(uv, va->guid, name, va->name_size); | 473 | + old_var = uefi_vars_find_variable(uv, va->guid, name, va->name_size); |
398 | + if (va->data_size) { | 474 | + if (va->data_size) { |
399 | + new_var = add_variable(uv, va->guid, name, va->name_size, | 475 | + new_var = add_variable(uv, va->guid, name, va->name_size, |
400 | + va->attributes); | 476 | + va->attributes); |
401 | + new_var->data = g_malloc(va->data_size); | 477 | + if (va->attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) { |
402 | + memcpy(new_var->data, data, va->data_size); | 478 | + /* not implemented (deprecated in uefi spec) */ |
403 | + new_var->data_size = va->data_size; | 479 | + warn_report("%s: AUTHENTICATED_WRITE_ACCESS", __func__); |
480 | + mvar->status = EFI_UNSUPPORTED; | ||
481 | + goto rollback; | ||
482 | + } else if (va->attributes & | ||
483 | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { | ||
484 | + status = uefi_vars_check_auth_2(uv, new_var, va, data); | ||
485 | + if (status != EFI_SUCCESS) { | ||
486 | + mvar->status = status; | ||
487 | + goto rollback; | ||
488 | + } | ||
489 | + if (old_var && new_var) { | ||
490 | + if (uefi_time_compare(&old_var->time, &new_var->time) > 0) { | ||
491 | + trace_uefi_vars_security_violation("time check failed"); | ||
492 | + mvar->status = EFI_SECURITY_VIOLATION; | ||
493 | + goto rollback; | ||
494 | + } | ||
495 | + if (old_var->digest_size || new_var->digest_size) { | ||
496 | + if (!uefi_vars_mm_digest_compare(old_var, new_var)) { | ||
497 | + mvar->status = EFI_SECURITY_VIOLATION; | ||
498 | + goto rollback; | ||
499 | + } | ||
500 | + } | ||
501 | + } | ||
502 | + } else { | ||
503 | + new_var->data = g_malloc(va->data_size); | ||
504 | + memcpy(new_var->data, data, va->data_size); | ||
505 | + new_var->data_size = va->data_size; | ||
506 | + } | ||
507 | + if (!new_var->data) { | ||
508 | + /* we land here when deleting authenticated variables */ | ||
509 | + del_variable(uv, new_var); | ||
510 | + new_var = NULL; | ||
511 | + } | ||
404 | + } else { | 512 | + } else { |
405 | + new_var = NULL; | 513 | + new_var = NULL; |
406 | + } | 514 | + } |
407 | + | 515 | + |
408 | + if (!old_var && !new_var) { | 516 | + if (!old_var && !new_var) { |
... | ... | ||
416 | + if (status != EFI_SUCCESS) { | 524 | + if (status != EFI_SUCCESS) { |
417 | + mvar->status = status; | 525 | + mvar->status = status; |
418 | + goto rollback; | 526 | + goto rollback; |
419 | + } | 527 | + } |
420 | + | 528 | + |
529 | + if (va->attributes & EFI_VARIABLE_APPEND_WRITE && old_var && new_var) { | ||
530 | + /* merge signature databases */ | ||
531 | + if (!uefi_vars_is_sb_any(new_var)) { | ||
532 | + mvar->status = EFI_UNSUPPORTED; | ||
533 | + goto rollback; | ||
534 | + } | ||
535 | + append_write(old_var, new_var); | ||
536 | + } | ||
537 | + | ||
421 | + /* check storage space */ | 538 | + /* check storage space */ |
422 | + new_storage = uv->used_storage; | 539 | + new_storage = uv->used_storage; |
423 | + if (old_var) { | 540 | + if (old_var) { |
424 | + new_storage -= variable_size(old_var); | 541 | + new_storage -= variable_size(old_var); |
425 | + } | 542 | + } |
... | ... | ||
441 | + | 558 | + |
442 | + if (attributes & EFI_VARIABLE_NON_VOLATILE) { | 559 | + if (attributes & EFI_VARIABLE_NON_VOLATILE) { |
443 | + uefi_vars_json_save(uv); | 560 | + uefi_vars_json_save(uv); |
444 | + } | 561 | + } |
445 | + | 562 | + |
563 | + if (new_var && uefi_vars_is_sb_pk(new_var)) { | ||
564 | + uefi_vars_auth_init(uv); | ||
565 | + } | ||
566 | + | ||
446 | + mvar->status = EFI_SUCCESS; | 567 | + mvar->status = EFI_SUCCESS; |
447 | + return sizeof(*mvar); | 568 | + return sizeof(*mvar); |
448 | + | 569 | + |
449 | +rollback: | 570 | +rollback: |
450 | + del_variable(uv, new_var); | 571 | + del_variable(uv, new_var); |
... | ... | ||
453 | + | 574 | + |
454 | +static size_t uefi_vars_mm_variable_info(uefi_vars_state *uv, mm_header *mhdr, | 575 | +static size_t uefi_vars_mm_variable_info(uefi_vars_state *uv, mm_header *mhdr, |
455 | + mm_variable *mvar, void *func) | 576 | + mm_variable *mvar, void *func) |
456 | +{ | 577 | +{ |
457 | + mm_variable_info *vi = func; | 578 | + mm_variable_info *vi = func; |
458 | + size_t length; | 579 | + uint64_t length; |
459 | + | 580 | + |
460 | + length = sizeof(*mvar) + sizeof(*vi); | 581 | + length = sizeof(*mvar) + sizeof(*vi); |
461 | + if (uv->buf_size < length) { | 582 | + if (uv->buf_size < length) { |
462 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 583 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
463 | + } | 584 | + } |
... | ... | ||
474 | +static size_t | 595 | +static size_t |
475 | +uefi_vars_mm_get_payload_size(uefi_vars_state *uv, mm_header *mhdr, | 596 | +uefi_vars_mm_get_payload_size(uefi_vars_state *uv, mm_header *mhdr, |
476 | + mm_variable *mvar, void *func) | 597 | + mm_variable *mvar, void *func) |
477 | +{ | 598 | +{ |
478 | + mm_get_payload_size *ps = func; | 599 | + mm_get_payload_size *ps = func; |
479 | + size_t length; | 600 | + uint64_t length; |
480 | + | 601 | + |
481 | + length = sizeof(*mvar) + sizeof(*ps); | 602 | + length = sizeof(*mvar) + sizeof(*ps); |
482 | + if (uv->buf_size < length) { | 603 | + if (uv->buf_size < length) { |
483 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 604 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
484 | + } | 605 | + } |
... | ... | ||
493 | + mm_variable *mvar, void *func) | 614 | + mm_variable *mvar, void *func) |
494 | +{ | 615 | +{ |
495 | + mm_lock_variable *lv = func; | 616 | + mm_lock_variable *lv = func; |
496 | + variable_policy_entry *pe; | 617 | + variable_policy_entry *pe; |
497 | + uint16_t *name, *dest; | 618 | + uint16_t *name, *dest; |
498 | + size_t length; | 619 | + uint64_t length; |
499 | + | 620 | + |
500 | + length = sizeof(*mvar) + sizeof(*lv); | 621 | + length = sizeof(*mvar) + sizeof(*lv); |
501 | + if (mhdr->length < length) { | 622 | + if (mhdr->length < length) { |
502 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 623 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
503 | + } | 624 | + } |
504 | + | 625 | + |
505 | + name = func + sizeof(*lv); | 626 | + name = func + sizeof(*lv); |
506 | + length += lv->name_size; | 627 | + if (uadd64_overflow(length, lv->name_size, &length)) { |
628 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | ||
629 | + } | ||
507 | + if (mhdr->length < length) { | 630 | + if (mhdr->length < length) { |
508 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); | 631 | + return uefi_vars_mm_error(mhdr, mvar, EFI_BAD_BUFFER_SIZE); |
509 | + } | 632 | + } |
510 | + | 633 | + |
511 | + uefi_trace_variable(__func__, lv->guid, name, lv->name_size); | 634 | + uefi_trace_variable(__func__, lv->guid, name, lv->name_size); |
... | ... | ||
549 | + "init-runtime-cache-contect", | 672 | + "init-runtime-cache-contect", |
550 | + "sync-runtime-cache", | 673 | + "sync-runtime-cache", |
551 | + "get-runtime-cache-info", | 674 | + "get-runtime-cache-info", |
552 | + }; | 675 | + }; |
553 | + const char *fname; | 676 | + const char *fname; |
554 | + size_t length; | 677 | + uint64_t length; |
555 | + | 678 | + |
556 | + mm_header *mhdr = (mm_header *) uv->buffer; | 679 | + mm_header *mhdr = (mm_header *) uv->buffer; |
557 | + mm_variable *mvar = (mm_variable *) (uv->buffer + sizeof(*mhdr)); | 680 | + mm_variable *mvar = (mm_variable *) (uv->buffer + sizeof(*mhdr)); |
558 | + void *func = (uv->buffer + sizeof(*mhdr) + sizeof(*mvar)); | 681 | + void *func = (uv->buffer + sizeof(*mhdr) + sizeof(*mvar)); |
559 | + | 682 | + |
... | ... | ||
614 | + | 737 | + |
615 | + uefi_trace_status(__func__, mvar->status); | 738 | + uefi_trace_status(__func__, mvar->status); |
616 | + return UEFI_VARS_STS_SUCCESS; | 739 | + return UEFI_VARS_STS_SUCCESS; |
617 | +} | 740 | +} |
618 | -- | 741 | -- |
619 | 2.41.0 | 742 | 2.47.1 | diff view generated by jsdifflib |
1 | This implements authenticated variable handling (AuthVariableLib in edk2). | 1 | This implements authenticated variable handling (see AuthVariableLib in |
---|---|---|---|
2 | edk2). | ||
2 | 3 | ||
3 | For now this implements the bare minimum to make secure boot work, | 4 | The by far most common use case for auth variables is secure boot. The |
4 | by initializing the 'SecureBoot' variable. | 5 | secure boot certificate databases ('PK', 'KEK', 'db' and 'dbx') are |
5 | 6 | authenticated variables, with update rules being specified in the UEFI | |
6 | Support for authenticated variable updates is not implemented yet, for | 7 | specification. |
7 | now they are read-only so the guest can neither provision secure boot | ||
8 | keys nor update the 'dbx' database. | ||
9 | 8 | ||
10 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 9 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
11 | --- | 10 | --- |
12 | hw/uefi/var-service-auth.c | 91 ++++++++++++++++++++++++++++++++++++++ | 11 | hw/uefi/var-service-auth.c | 361 +++++++++++++++++++++++++++++++++++++ |
13 | 1 file changed, 91 insertions(+) | 12 | 1 file changed, 361 insertions(+) |
14 | create mode 100644 hw/uefi/var-service-auth.c | 13 | create mode 100644 hw/uefi/var-service-auth.c |
15 | 14 | ||
16 | diff --git a/hw/uefi/var-service-auth.c b/hw/uefi/var-service-auth.c | 15 | diff --git a/hw/uefi/var-service-auth.c b/hw/uefi/var-service-auth.c |
17 | new file mode 100644 | 16 | new file mode 100644 |
18 | index XXXXXXX..XXXXXXX | 17 | index XXXXXXX..XXXXXXX |
... | ... | ||
24 | + * | 23 | + * |
25 | + * uefi vars device - AuthVariableLib | 24 | + * uefi vars device - AuthVariableLib |
26 | + */ | 25 | + */ |
27 | + | 26 | + |
28 | +#include "qemu/osdep.h" | 27 | +#include "qemu/osdep.h" |
29 | +#include "sysemu/dma.h" | 28 | +#include "qemu/error-report.h" |
29 | +#include "system/dma.h" | ||
30 | + | 30 | + |
31 | +#include "hw/uefi/var-service.h" | 31 | +#include "hw/uefi/var-service.h" |
32 | + | 32 | + |
33 | +static const uint16_t name_pk[] = { 'P', 'K', | 33 | +static const uint16_t name_pk[] = u"PK"; |
34 | + 0 }; | 34 | +static const uint16_t name_kek[] = u"KEK"; |
35 | +static const uint16_t name_setup_mode[] = { 'S', 'e', 't', 'u', 'p', | 35 | +static const uint16_t name_db[] = u"db"; |
36 | + 'M', 'o', 'd', 'e', | 36 | +static const uint16_t name_dbx[] = u"dbx"; |
37 | + 0 }; | 37 | +static const uint16_t name_setup_mode[] = u"SetupMode"; |
38 | +static const uint16_t name_sb[] = { 'S', 'e', 'c', 'u', 'r', 'e', | 38 | +static const uint16_t name_sigs_support[] = u"SignatureSupport"; |
39 | + 'B', 'o', 'o', 't', | 39 | +static const uint16_t name_sb[] = u"SecureBoot"; |
40 | + 0 }; | 40 | +static const uint16_t name_sb_enable[] = u"SecureBootEnable"; |
41 | +static const uint16_t name_sb_enable[] = { 'S', 'e', 'c', 'u', 'r', 'e', | 41 | +static const uint16_t name_custom_mode[] = u"CustomMode"; |
42 | + 'B', 'o', 'o', 't', | 42 | +static const uint16_t name_vk[] = u"VendorKeys"; |
43 | + 'E', 'n', 'a', 'b', 'l', 'e', | 43 | +static const uint16_t name_vk_nv[] = u"VendorKeysNv"; |
44 | + 0 }; | 44 | + |
45 | +static const uint16_t name_custom_mode[] = { 'C', 'u', 's', 't', 'o', 'm', | 45 | +static const uint32_t sigdb_attrs = |
46 | + 'M', 'o', 'd', 'e', | 46 | + EFI_VARIABLE_NON_VOLATILE | |
47 | + 0 }; | 47 | + EFI_VARIABLE_BOOTSERVICE_ACCESS | |
48 | + EFI_VARIABLE_RUNTIME_ACCESS | | ||
49 | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; | ||
50 | + | ||
51 | +static void set_secure_boot(uefi_vars_state *uv, uint8_t sb) | ||
52 | +{ | ||
53 | + uefi_vars_set_variable(uv, EfiGlobalVariable, | ||
54 | + name_sb, sizeof(name_sb), | ||
55 | + EFI_VARIABLE_BOOTSERVICE_ACCESS | | ||
56 | + EFI_VARIABLE_RUNTIME_ACCESS, | ||
57 | + &sb, sizeof(sb)); | ||
58 | +} | ||
59 | + | ||
60 | +static void set_secure_boot_enable(uefi_vars_state *uv, uint8_t sbe) | ||
61 | +{ | ||
62 | + uefi_vars_set_variable(uv, EfiSecureBootEnableDisable, | ||
63 | + name_sb_enable, sizeof(name_sb_enable), | ||
64 | + EFI_VARIABLE_NON_VOLATILE | | ||
65 | + EFI_VARIABLE_BOOTSERVICE_ACCESS, | ||
66 | + &sbe, sizeof(sbe)); | ||
67 | +} | ||
68 | + | ||
69 | +static void set_setup_mode(uefi_vars_state *uv, uint8_t sm) | ||
70 | +{ | ||
71 | + uefi_vars_set_variable(uv, EfiGlobalVariable, | ||
72 | + name_setup_mode, sizeof(name_setup_mode), | ||
73 | + EFI_VARIABLE_BOOTSERVICE_ACCESS | | ||
74 | + EFI_VARIABLE_RUNTIME_ACCESS, | ||
75 | + &sm, sizeof(sm)); | ||
76 | +} | ||
77 | + | ||
78 | +static void set_custom_mode(uefi_vars_state *uv, uint8_t cm) | ||
79 | +{ | ||
80 | + uefi_vars_set_variable(uv, EfiCustomModeEnable, | ||
81 | + name_custom_mode, sizeof(name_custom_mode), | ||
82 | + EFI_VARIABLE_NON_VOLATILE | | ||
83 | + EFI_VARIABLE_BOOTSERVICE_ACCESS, | ||
84 | + &cm, sizeof(cm)); | ||
85 | +} | ||
86 | + | ||
87 | +static void set_signature_support(uefi_vars_state *uv) | ||
88 | +{ | ||
89 | + QemuUUID sigs_support[5]; | ||
90 | + | ||
91 | + sigs_support[0] = EfiCertSha256Guid; | ||
92 | + sigs_support[1] = EfiCertSha384Guid; | ||
93 | + sigs_support[2] = EfiCertSha512Guid; | ||
94 | + sigs_support[3] = EfiCertRsa2048Guid; | ||
95 | + sigs_support[4] = EfiCertX509Guid; | ||
96 | + | ||
97 | + uefi_vars_set_variable(uv, EfiGlobalVariable, | ||
98 | + name_sigs_support, sizeof(name_sigs_support), | ||
99 | + EFI_VARIABLE_BOOTSERVICE_ACCESS | | ||
100 | + EFI_VARIABLE_RUNTIME_ACCESS, | ||
101 | + sigs_support, sizeof(sigs_support)); | ||
102 | +} | ||
103 | + | ||
104 | +static bool setup_mode_is_active(uefi_vars_state *uv) | ||
105 | +{ | ||
106 | + uefi_variable *var; | ||
107 | + uint8_t *value; | ||
108 | + | ||
109 | + var = uefi_vars_find_variable(uv, EfiGlobalVariable, | ||
110 | + name_setup_mode, sizeof(name_setup_mode)); | ||
111 | + if (var) { | ||
112 | + value = var->data; | ||
113 | + if (value[0] == SETUP_MODE) { | ||
114 | + return true; | ||
115 | + } | ||
116 | + } | ||
117 | + return false; | ||
118 | +} | ||
119 | + | ||
120 | +static bool custom_mode_is_active(uefi_vars_state *uv) | ||
121 | +{ | ||
122 | + uefi_variable *var; | ||
123 | + uint8_t *value; | ||
124 | + | ||
125 | + var = uefi_vars_find_variable(uv, EfiCustomModeEnable, | ||
126 | + name_custom_mode, sizeof(name_custom_mode)); | ||
127 | + if (var) { | ||
128 | + value = var->data; | ||
129 | + if (value[0] == CUSTOM_SECURE_BOOT_MODE) { | ||
130 | + return true; | ||
131 | + } | ||
132 | + } | ||
133 | + return false; | ||
134 | +} | ||
135 | + | ||
136 | +bool uefi_vars_is_sb_pk(uefi_variable *var) | ||
137 | +{ | ||
138 | + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && | ||
139 | + uefi_str_equal(var->name, var->name_size, name_pk, sizeof(name_pk))) { | ||
140 | + return true; | ||
141 | + } | ||
142 | + return false; | ||
143 | +} | ||
144 | + | ||
145 | +static bool uefi_vars_is_sb_kek(uefi_variable *var) | ||
146 | +{ | ||
147 | + if (qemu_uuid_is_equal(&var->guid, &EfiGlobalVariable) && | ||
148 | + uefi_str_equal(var->name, var->name_size, name_kek, sizeof(name_kek))) { | ||
149 | + return true; | ||
150 | + } | ||
151 | + return false; | ||
152 | +} | ||
153 | + | ||
154 | +static bool uefi_vars_is_sb_db(uefi_variable *var) | ||
155 | +{ | ||
156 | + if (!qemu_uuid_is_equal(&var->guid, &EfiImageSecurityDatabase)) { | ||
157 | + return false; | ||
158 | + } | ||
159 | + if (uefi_str_equal(var->name, var->name_size, name_db, sizeof(name_db))) { | ||
160 | + return true; | ||
161 | + } | ||
162 | + if (uefi_str_equal(var->name, var->name_size, name_dbx, sizeof(name_dbx))) { | ||
163 | + return true; | ||
164 | + } | ||
165 | + return false; | ||
166 | +} | ||
167 | + | ||
168 | +bool uefi_vars_is_sb_any(uefi_variable *var) | ||
169 | +{ | ||
170 | + if (uefi_vars_is_sb_pk(var) || | ||
171 | + uefi_vars_is_sb_kek(var) || | ||
172 | + uefi_vars_is_sb_db(var)) { | ||
173 | + return true; | ||
174 | + } | ||
175 | + return false; | ||
176 | +} | ||
177 | + | ||
178 | +static uefi_variable *uefi_vars_find_siglist(uefi_vars_state *uv, | ||
179 | + uefi_variable *var) | ||
180 | +{ | ||
181 | + if (uefi_vars_is_sb_pk(var)) { | ||
182 | + return uefi_vars_find_variable(uv, EfiGlobalVariable, | ||
183 | + name_pk, sizeof(name_pk)); | ||
184 | + } | ||
185 | + if (uefi_vars_is_sb_kek(var)) { | ||
186 | + return uefi_vars_find_variable(uv, EfiGlobalVariable, | ||
187 | + name_pk, sizeof(name_pk)); | ||
188 | + } | ||
189 | + if (uefi_vars_is_sb_db(var)) { | ||
190 | + return uefi_vars_find_variable(uv, EfiGlobalVariable, | ||
191 | + name_kek, sizeof(name_kek)); | ||
192 | + } | ||
193 | + | ||
194 | + return NULL; | ||
195 | +} | ||
196 | + | ||
197 | +static efi_status uefi_vars_check_auth_2_sb(uefi_vars_state *uv, | ||
198 | + uefi_variable *var, | ||
199 | + mm_variable_access *va, | ||
200 | + void *data, | ||
201 | + uint64_t data_offset) | ||
202 | +{ | ||
203 | + variable_auth_2 *auth = data; | ||
204 | + uefi_variable *siglist; | ||
205 | + | ||
206 | + if (custom_mode_is_active(uv)) { | ||
207 | + /* no authentication in custom mode */ | ||
208 | + return EFI_SUCCESS; | ||
209 | + } | ||
210 | + | ||
211 | + if (setup_mode_is_active(uv) && !uefi_vars_is_sb_pk(var)) { | ||
212 | + /* no authentication in setup mode (except PK) */ | ||
213 | + return EFI_SUCCESS; | ||
214 | + } | ||
215 | + | ||
216 | + if (auth->hdr_length == 24) { | ||
217 | + /* no signature (auth->cert_data is empty) */ | ||
218 | + return EFI_SECURITY_VIOLATION; | ||
219 | + } | ||
220 | + | ||
221 | + siglist = uefi_vars_find_siglist(uv, var); | ||
222 | + if (!siglist && setup_mode_is_active(uv) && uefi_vars_is_sb_pk(var)) { | ||
223 | + /* check PK is self-signed */ | ||
224 | + uefi_variable tmp = { | ||
225 | + .guid = EfiGlobalVariable, | ||
226 | + .name = (uint16_t *)name_pk, | ||
227 | + .name_size = sizeof(name_pk), | ||
228 | + .attributes = sigdb_attrs, | ||
229 | + .data = data + data_offset, | ||
230 | + .data_size = va->data_size - data_offset, | ||
231 | + }; | ||
232 | + return uefi_vars_check_pkcs7_2(&tmp, NULL, NULL, va, data); | ||
233 | + } | ||
234 | + | ||
235 | + return uefi_vars_check_pkcs7_2(siglist, NULL, NULL, va, data); | ||
236 | +} | ||
237 | + | ||
238 | +efi_status uefi_vars_check_auth_2(uefi_vars_state *uv, uefi_variable *var, | ||
239 | + mm_variable_access *va, void *data) | ||
240 | +{ | ||
241 | + variable_auth_2 *auth = data; | ||
242 | + uint64_t data_offset; | ||
243 | + efi_status status; | ||
244 | + | ||
245 | + if (va->data_size < sizeof(*auth)) { | ||
246 | + return EFI_SECURITY_VIOLATION; | ||
247 | + } | ||
248 | + if (uadd64_overflow(sizeof(efi_time), auth->hdr_length, &data_offset)) { | ||
249 | + return EFI_SECURITY_VIOLATION; | ||
250 | + } | ||
251 | + if (va->data_size < data_offset) { | ||
252 | + return EFI_SECURITY_VIOLATION; | ||
253 | + } | ||
254 | + | ||
255 | + if (auth->hdr_revision != 0x0200 || | ||
256 | + auth->hdr_cert_type != WIN_CERT_TYPE_EFI_GUID || | ||
257 | + !qemu_uuid_is_equal(&auth->guid_cert_type, &EfiCertTypePkcs7Guid)) { | ||
258 | + return EFI_UNSUPPORTED; | ||
259 | + } | ||
260 | + | ||
261 | + if (uefi_vars_is_sb_any(var)) { | ||
262 | + /* secure boot variables */ | ||
263 | + status = uefi_vars_check_auth_2_sb(uv, var, va, data, data_offset); | ||
264 | + if (status != EFI_SUCCESS) { | ||
265 | + return status; | ||
266 | + } | ||
267 | + } else { | ||
268 | + /* other authenticated variables */ | ||
269 | + status = uefi_vars_check_pkcs7_2(NULL, | ||
270 | + &var->digest, &var->digest_size, | ||
271 | + va, data); | ||
272 | + if (status != EFI_SUCCESS) { | ||
273 | + return status; | ||
274 | + } | ||
275 | + } | ||
276 | + | ||
277 | + /* checks passed, set variable data */ | ||
278 | + var->time = auth->timestamp; | ||
279 | + if (va->data_size - data_offset > 0) { | ||
280 | + var->data = g_malloc(va->data_size - data_offset); | ||
281 | + memcpy(var->data, data + data_offset, va->data_size - data_offset); | ||
282 | + var->data_size = va->data_size - data_offset; | ||
283 | + } | ||
284 | + | ||
285 | + return EFI_SUCCESS; | ||
286 | +} | ||
287 | + | ||
288 | +efi_status uefi_vars_check_secure_boot(uefi_vars_state *uv, uefi_variable *var) | ||
289 | +{ | ||
290 | + uint8_t *value = var->data; | ||
291 | + | ||
292 | + if (uefi_vars_is_sb_any(var)) { | ||
293 | + if (var->attributes != sigdb_attrs) { | ||
294 | + return EFI_INVALID_PARAMETER; | ||
295 | + } | ||
296 | + } | ||
297 | + | ||
298 | + /* reject SecureBootEnable updates if force_secure_boot is set */ | ||
299 | + if (qemu_uuid_is_equal(&var->guid, &EfiSecureBootEnableDisable) && | ||
300 | + uefi_str_equal(var->name, var->name_size, | ||
301 | + name_sb_enable, sizeof(name_sb_enable)) && | ||
302 | + uv->force_secure_boot && | ||
303 | + value[0] != SECURE_BOOT_ENABLE) { | ||
304 | + return EFI_WRITE_PROTECTED; | ||
305 | + } | ||
306 | + | ||
307 | + /* reject CustomMode updates if disable_custom_mode is set */ | ||
308 | + if (qemu_uuid_is_equal(&var->guid, &EfiCustomModeEnable) && | ||
309 | + uefi_str_equal(var->name, var->name_size, | ||
310 | + name_custom_mode, sizeof(name_custom_mode)) && | ||
311 | + uv->disable_custom_mode) { | ||
312 | + return EFI_WRITE_PROTECTED; | ||
313 | + } | ||
314 | + | ||
315 | + return EFI_SUCCESS; | ||
316 | +} | ||
48 | + | 317 | + |
49 | +/* AuthVariableLibInitialize */ | 318 | +/* AuthVariableLibInitialize */ |
50 | +void uefi_vars_auth_init(uefi_vars_state *uv) | 319 | +void uefi_vars_auth_init(uefi_vars_state *uv) |
51 | +{ | 320 | +{ |
52 | + uefi_variable *pk_var, *sbe_var;; | 321 | + uefi_variable *pk_var, *sbe_var; |
53 | + uint8_t platform_mode, sb, sbe, custom_mode; | 322 | + uint8_t platform_mode, sb, sbe, vk; |
54 | + | 323 | + |
55 | + /* SetupMode */ | 324 | + /* SetupMode */ |
56 | + pk_var = uefi_vars_find_variable(uv, EfiGlobalVariable, | 325 | + pk_var = uefi_vars_find_variable(uv, EfiGlobalVariable, |
57 | + name_pk, sizeof(name_pk)); | 326 | + name_pk, sizeof(name_pk)); |
58 | + if (!pk_var) { | 327 | + if (!pk_var) { |
59 | + platform_mode = SETUP_MODE; | 328 | + platform_mode = SETUP_MODE; |
60 | + } else { | 329 | + } else { |
61 | + platform_mode = USER_MODE; | 330 | + platform_mode = USER_MODE; |
62 | + } | 331 | + } |
63 | + uefi_vars_set_variable(uv, EfiGlobalVariable, | 332 | + set_setup_mode(uv, platform_mode); |
64 | + name_setup_mode, sizeof(name_setup_mode), | 333 | + |
65 | + EFI_VARIABLE_BOOTSERVICE_ACCESS | | 334 | + /* SignatureSupport */ |
66 | + EFI_VARIABLE_RUNTIME_ACCESS, | 335 | + set_signature_support(uv); |
67 | + &platform_mode, sizeof(platform_mode)); | ||
68 | + | ||
69 | + /* TODO: SignatureSupport */ | ||
70 | + | 336 | + |
71 | + /* SecureBootEnable */ | 337 | + /* SecureBootEnable */ |
72 | + sbe = SECURE_BOOT_DISABLE; | 338 | + sbe = SECURE_BOOT_DISABLE; |
73 | + sbe_var = uefi_vars_find_variable(uv, EfiSecureBootEnableDisable, | 339 | + sbe_var = uefi_vars_find_variable(uv, EfiSecureBootEnableDisable, |
74 | + name_sb_enable, sizeof(name_sb_enable)); | 340 | + name_sb_enable, sizeof(name_sb_enable)); |
75 | + if (sbe_var) { | 341 | + if (sbe_var) { |
76 | + if (platform_mode == USER_MODE) { | 342 | + if (platform_mode == USER_MODE) { |
77 | + sbe = ((uint8_t*)sbe_var->data)[0]; | 343 | + sbe = ((uint8_t *)sbe_var->data)[0]; |
78 | + } | 344 | + } |
79 | + } else if (platform_mode == USER_MODE) { | 345 | + } else if (platform_mode == USER_MODE) { |
80 | + sbe = SECURE_BOOT_ENABLE; | 346 | + sbe = SECURE_BOOT_ENABLE; |
81 | + uefi_vars_set_variable(uv, EfiSecureBootEnableDisable, | 347 | + set_secure_boot_enable(uv, sbe); |
82 | + name_sb_enable, sizeof(name_sb_enable), | 348 | + } |
83 | + EFI_VARIABLE_NON_VOLATILE | | 349 | + |
84 | + EFI_VARIABLE_BOOTSERVICE_ACCESS, | 350 | + if (uv->force_secure_boot && sbe != SECURE_BOOT_ENABLE) { |
85 | + &sbe, sizeof(sbe)); | 351 | + sbe = SECURE_BOOT_ENABLE; |
352 | + set_secure_boot_enable(uv, sbe); | ||
86 | + } | 353 | + } |
87 | + | 354 | + |
88 | + /* SecureBoot */ | 355 | + /* SecureBoot */ |
89 | + if ((sbe == SECURE_BOOT_ENABLE) && (platform_mode == USER_MODE)) { | 356 | + if ((sbe == SECURE_BOOT_ENABLE) && (platform_mode == USER_MODE)) { |
90 | + sb = SECURE_BOOT_MODE_ENABLE; | 357 | + sb = SECURE_BOOT_MODE_ENABLE; |
91 | + } else { | 358 | + } else { |
92 | + sb = SECURE_BOOT_MODE_DISABLE; | 359 | + sb = SECURE_BOOT_MODE_DISABLE; |
93 | + } | 360 | + } |
94 | + uefi_vars_set_variable(uv, EfiGlobalVariable, | 361 | + set_secure_boot(uv, sb); |
95 | + name_sb, sizeof(name_sb), | 362 | + |
363 | + /* CustomMode */ | ||
364 | + set_custom_mode(uv, STANDARD_SECURE_BOOT_MODE); | ||
365 | + | ||
366 | + vk = 0; | ||
367 | + uefi_vars_set_variable(uv, EfiGlobalVariable, | ||
368 | + name_vk_nv, sizeof(name_vk_nv), | ||
369 | + EFI_VARIABLE_NON_VOLATILE | | ||
370 | + EFI_VARIABLE_BOOTSERVICE_ACCESS | | ||
371 | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS, | ||
372 | + &vk, sizeof(vk)); | ||
373 | + uefi_vars_set_variable(uv, EfiGlobalVariable, | ||
374 | + name_vk, sizeof(name_vk), | ||
96 | + EFI_VARIABLE_BOOTSERVICE_ACCESS | | 375 | + EFI_VARIABLE_BOOTSERVICE_ACCESS | |
97 | + EFI_VARIABLE_RUNTIME_ACCESS, | 376 | + EFI_VARIABLE_RUNTIME_ACCESS, |
98 | + &sb, sizeof(sb)); | 377 | + &vk, sizeof(vk)); |
99 | + | 378 | + |
100 | + /* CustomMode */ | 379 | + /* flush to disk */ |
101 | + custom_mode = STANDARD_SECURE_BOOT_MODE; | 380 | + uefi_vars_json_save(uv); |
102 | + uefi_vars_set_variable(uv, EfiCustomModeEnable, | ||
103 | + name_custom_mode, sizeof(name_custom_mode), | ||
104 | + EFI_VARIABLE_NON_VOLATILE | | ||
105 | + EFI_VARIABLE_BOOTSERVICE_ACCESS, | ||
106 | + &custom_mode, sizeof(custom_mode)); | ||
107 | + | ||
108 | + /* TODO: certdb */ | ||
109 | + /* TODO: certdbv */ | ||
110 | + /* TODO: VendorKeysNv */ | ||
111 | + /* TODO: VendorKeys */ | ||
112 | +} | 381 | +} |
113 | -- | 382 | -- |
114 | 2.41.0 | 383 | 2.47.1 | diff view generated by jsdifflib |
1 | Implement variable policies (Edk2VariablePolicyProtocol). | 1 | Implement variable policies (Edk2VariablePolicyProtocol). |
---|---|---|---|
2 | 2 | ||
3 | This protocol allows to define restrictions for variables. | 3 | This EFI protocol allows to define restrictions for variables. |
4 | It also allows to lock down variables (disallow write access). | 4 | It also allows to lock down variables (disallow write access). |
5 | 5 | ||
6 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 6 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
7 | --- | 7 | --- |
8 | hw/uefi/var-service-policy.c | 390 +++++++++++++++++++++++++++++++++++ | 8 | hw/uefi/var-service-policy.c | 370 +++++++++++++++++++++++++++++++++++ |
9 | 1 file changed, 390 insertions(+) | 9 | 1 file changed, 370 insertions(+) |
10 | create mode 100644 hw/uefi/var-service-policy.c | 10 | create mode 100644 hw/uefi/var-service-policy.c |
11 | 11 | ||
12 | diff --git a/hw/uefi/var-service-policy.c b/hw/uefi/var-service-policy.c | 12 | diff --git a/hw/uefi/var-service-policy.c b/hw/uefi/var-service-policy.c |
13 | new file mode 100644 | 13 | new file mode 100644 |
14 | index XXXXXXX..XXXXXXX | 14 | index XXXXXXX..XXXXXXX |
... | ... | ||
22 | + * | 22 | + * |
23 | + * variable policy specs: | 23 | + * variable policy specs: |
24 | + * https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md | 24 | + * https://github.com/tianocore/edk2/blob/master/MdeModulePkg/Library/VariablePolicyLib/ReadMe.md |
25 | + */ | 25 | + */ |
26 | +#include "qemu/osdep.h" | 26 | +#include "qemu/osdep.h" |
27 | +#include "sysemu/dma.h" | 27 | +#include "system/dma.h" |
28 | +#include "migration/vmstate.h" | 28 | +#include "migration/vmstate.h" |
29 | + | 29 | + |
30 | +#include "hw/uefi/var-service.h" | 30 | +#include "hw/uefi/var-service.h" |
31 | +#include "hw/uefi/var-service-api.h" | 31 | +#include "hw/uefi/var-service-api.h" |
32 | +#include "hw/uefi/var-service-edk2.h" | 32 | +#include "hw/uefi/var-service-edk2.h" |
... | ... | ||
83 | + if (pe->lock_policy_type) { | 83 | + if (pe->lock_policy_type) { |
84 | + fprintf(stderr, " lock policy type %d\n", pe->lock_policy_type); | 84 | + fprintf(stderr, " lock policy type %d\n", pe->lock_policy_type); |
85 | + } | 85 | + } |
86 | +} | 86 | +} |
87 | + | 87 | + |
88 | +static gboolean wildcard_strcmp(uefi_var_policy *pol, | 88 | +static gboolean wildcard_str_equal(uefi_var_policy *pol, |
89 | + uefi_variable *var) | 89 | + uefi_variable *var) |
90 | +{ | 90 | +{ |
91 | + size_t pos = 0; | 91 | + return uefi_str_equal_ex(pol->name, pol->name_size, |
92 | + size_t plen = pol->name_size / 2; | 92 | + var->name, var->name_size, |
93 | + size_t vlen = var->name_size / 2; | 93 | + true); |
94 | + | ||
95 | + if (plen == 0) { | ||
96 | + return true; | ||
97 | + } | ||
98 | + | ||
99 | + for (;;) { | ||
100 | + if (pos == plen && pos == vlen) { | ||
101 | + return true; | ||
102 | + } | ||
103 | + if (pos == plen || pos == vlen) { | ||
104 | + return false; | ||
105 | + } | ||
106 | + if (pol->name[pos] == 0 && var->name[pos] == 0) { | ||
107 | + return true; | ||
108 | + } | ||
109 | + | ||
110 | + if (pol->name[pos] == '#') { | ||
111 | + if (!isxdigit(var->name[pos])) { | ||
112 | + return false; | ||
113 | + } | ||
114 | + } else { | ||
115 | + if (pol->name[pos] != var->name[pos]) { | ||
116 | + return false; | ||
117 | + } | ||
118 | + } | ||
119 | + | ||
120 | + pos++; | ||
121 | + } | ||
122 | +} | 94 | +} |
123 | + | 95 | + |
124 | +static uefi_var_policy *find_policy(uefi_vars_state *uv, QemuUUID guid, | 96 | +static uefi_var_policy *find_policy(uefi_vars_state *uv, QemuUUID guid, |
125 | + uint16_t *name, uint64_t name_size) | 97 | + uint16_t *name, uint64_t name_size) |
126 | +{ | 98 | +{ |
... | ... | ||
146 | + | 118 | + |
147 | + QTAILQ_FOREACH(pol, &uv->var_policies, next) { | 119 | + QTAILQ_FOREACH(pol, &uv->var_policies, next) { |
148 | + if (!qemu_uuid_is_equal(&pol->entry->namespace, &var->guid)) { | 120 | + if (!qemu_uuid_is_equal(&pol->entry->namespace, &var->guid)) { |
149 | + continue; | 121 | + continue; |
150 | + } | 122 | + } |
151 | + if (!wildcard_strcmp(pol, var)) { | 123 | + if (!wildcard_str_equal(pol, var)) { |
152 | + continue; | 124 | + continue; |
153 | + } | 125 | + } |
154 | + return pol; | 126 | + return pol; |
155 | + } | 127 | + } |
156 | + return NULL; | 128 | + return NULL; |
... | ... | ||
317 | + mm_check_policy *mchk, | 289 | + mm_check_policy *mchk, |
318 | + void *func) | 290 | + void *func) |
319 | +{ | 291 | +{ |
320 | + variable_policy_entry *pe = func; | 292 | + variable_policy_entry *pe = func; |
321 | + uefi_var_policy *pol; | 293 | + uefi_var_policy *pol; |
322 | + size_t length; | 294 | + uint64_t length; |
323 | + | 295 | + |
324 | + length = sizeof(*mchk) + pe->size; | 296 | + if (uadd64_overflow(sizeof(*mchk), pe->size, &length)) { |
297 | + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); | ||
298 | + } | ||
325 | + if (mhdr->length < length) { | 299 | + if (mhdr->length < length) { |
326 | + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); | 300 | + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); |
327 | + } | 301 | + } |
328 | + if (pe->size < sizeof(*pe)) { | 302 | + if (pe->size < sizeof(*pe)) { |
329 | + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); | 303 | + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); |
... | ... | ||
338 | + } | 312 | + } |
339 | + | 313 | + |
340 | + /* check space for minimum string length */ | 314 | + /* check space for minimum string length */ |
341 | + if (pe->size < (size_t)pe->offset_to_name) { | 315 | + if (pe->size < (size_t)pe->offset_to_name) { |
342 | + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); | 316 | + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_BAD_BUFFER_SIZE); |
317 | + } | ||
318 | + | ||
319 | + if (!uefi_str_is_valid((void *)pe + pe->offset_to_name, | ||
320 | + pe->size - pe->offset_to_name, | ||
321 | + false)) { | ||
322 | + return uefi_vars_mm_policy_error(mhdr, mchk, EFI_INVALID_PARAMETER); | ||
343 | + } | 323 | + } |
344 | + | 324 | + |
345 | + pol = find_policy(uv, pe->namespace, | 325 | + pol = find_policy(uv, pe->namespace, |
346 | + (void *)pe + pe->offset_to_name, | 326 | + (void *)pe + pe->offset_to_name, |
347 | + pe->size - pe->offset_to_name); | 327 | + pe->size - pe->offset_to_name); |
... | ... | ||
404 | + | 384 | + |
405 | + uefi_trace_status(__func__, mchk->result); | 385 | + uefi_trace_status(__func__, mchk->result); |
406 | + return UEFI_VARS_STS_SUCCESS; | 386 | + return UEFI_VARS_STS_SUCCESS; |
407 | +} | 387 | +} |
408 | -- | 388 | -- |
409 | 2.41.0 | 389 | 2.47.1 |
410 | 390 | ||
411 | 391 | diff view generated by jsdifflib |
1 | This is the core code for guest <-> host communication. This accepts | 1 | This is the core code for guest <-> host communication. This accepts |
---|---|---|---|
2 | request messages from the guest, dispatches them to the service called, | 2 | request messages from the guest, dispatches them to the service called, |
3 | and sends back the response message. | 3 | and sends back the response message. |
4 | 4 | ||
5 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 5 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
6 | --- | 6 | --- |
7 | hw/uefi/var-service-core.c | 350 +++++++++++++++++++++++++++++++++++++ | 7 | hw/uefi/var-service-core.c | 237 +++++++++++++++++++++++++++++++++++++ |
8 | 1 file changed, 350 insertions(+) | 8 | 1 file changed, 237 insertions(+) |
9 | create mode 100644 hw/uefi/var-service-core.c | 9 | create mode 100644 hw/uefi/var-service-core.c |
10 | 10 | ||
11 | diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c | 11 | diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c |
12 | new file mode 100644 | 12 | new file mode 100644 |
13 | index XXXXXXX..XXXXXXX | 13 | index XXXXXXX..XXXXXXX |
... | ... | ||
18 | + * SPDX-License-Identifier: GPL-2.0-or-later | 18 | + * SPDX-License-Identifier: GPL-2.0-or-later |
19 | + * | 19 | + * |
20 | + * uefi vars device | 20 | + * uefi vars device |
21 | + */ | 21 | + */ |
22 | +#include "qemu/osdep.h" | 22 | +#include "qemu/osdep.h" |
23 | +#include "sysemu/dma.h" | 23 | +#include "system/dma.h" |
24 | +#include "migration/vmstate.h" | 24 | +#include "migration/vmstate.h" |
25 | + | 25 | + |
26 | +#include "hw/uefi/var-service.h" | 26 | +#include "hw/uefi/var-service.h" |
27 | +#include "hw/uefi/var-service-api.h" | 27 | +#include "hw/uefi/var-service-api.h" |
28 | +#include "hw/uefi/var-service-edk2.h" | 28 | +#include "hw/uefi/var-service-edk2.h" |
... | ... | ||
68 | + vmstate_uefi_var_policy, uefi_var_policy, next), | 68 | + vmstate_uefi_var_policy, uefi_var_policy, next), |
69 | + VMSTATE_END_OF_LIST() | 69 | + VMSTATE_END_OF_LIST() |
70 | + }, | 70 | + }, |
71 | +}; | 71 | +}; |
72 | + | 72 | + |
73 | +size_t uefi_strlen(const uint16_t *str, size_t len) | ||
74 | +{ | ||
75 | + size_t pos = 0; | ||
76 | + | ||
77 | + for (;;) { | ||
78 | + if (pos == len) { | ||
79 | + return pos; | ||
80 | + } | ||
81 | + if (str[pos] == 0) { | ||
82 | + return pos; | ||
83 | + } | ||
84 | + pos++; | ||
85 | + } | ||
86 | +} | ||
87 | + | ||
88 | +gboolean uefi_str_equal(const uint16_t *a, size_t alen, | ||
89 | + const uint16_t *b, size_t blen) | ||
90 | +{ | ||
91 | + size_t pos = 0; | ||
92 | + | ||
93 | + alen = alen / 2; | ||
94 | + blen = blen / 2; | ||
95 | + for (;;) { | ||
96 | + if (pos == alen && pos == blen) { | ||
97 | + return true; | ||
98 | + } | ||
99 | + if (pos == alen && b[pos] == 0) { | ||
100 | + return true; | ||
101 | + } | ||
102 | + if (pos == blen && a[pos] == 0) { | ||
103 | + return true; | ||
104 | + } | ||
105 | + if (pos == alen || pos == blen) { | ||
106 | + return false; | ||
107 | + } | ||
108 | + if (a[pos] == 0 && b[pos] == 0) { | ||
109 | + return true; | ||
110 | + } | ||
111 | + if (a[pos] != b[pos]) { | ||
112 | + return false; | ||
113 | + } | ||
114 | + pos++; | ||
115 | + } | ||
116 | +} | ||
117 | + | ||
118 | +char *uefi_ucs2_to_ascii(const uint16_t *ucs2, uint64_t ucs2_size) | ||
119 | +{ | ||
120 | + char *str = g_malloc0(ucs2_size / 2 + 1); | ||
121 | + int i; | ||
122 | + | ||
123 | + for (i = 0; i * 2 < ucs2_size; i++) { | ||
124 | + if (ucs2[i] == 0) { | ||
125 | + break; | ||
126 | + } | ||
127 | + if (ucs2[i] < 128) { | ||
128 | + str[i] = ucs2[i]; | ||
129 | + } else { | ||
130 | + str[i] = '?'; | ||
131 | + } | ||
132 | + } | ||
133 | + str[i] = 0; | ||
134 | + return str; | ||
135 | +} | ||
136 | + | ||
137 | +void uefi_trace_variable(const char *action, QemuUUID guid, | ||
138 | + const uint16_t *name, uint64_t name_size) | ||
139 | +{ | ||
140 | + QemuUUID be = qemu_uuid_bswap(guid); | ||
141 | + char *str_uuid = qemu_uuid_unparse_strdup(&be); | ||
142 | + char *str_name = uefi_ucs2_to_ascii(name, name_size); | ||
143 | + | ||
144 | + trace_uefi_variable(action, str_name, name_size, str_uuid); | ||
145 | + | ||
146 | + g_free(str_name); | ||
147 | + g_free(str_uuid); | ||
148 | +} | ||
149 | + | ||
150 | +void uefi_trace_status(const char *action, efi_status status) | ||
151 | +{ | ||
152 | + switch (status) { | ||
153 | + case EFI_SUCCESS: | ||
154 | + trace_uefi_status(action, "success"); | ||
155 | + break; | ||
156 | + case EFI_INVALID_PARAMETER: | ||
157 | + trace_uefi_status(action, "invalid parameter"); | ||
158 | + break; | ||
159 | + case EFI_UNSUPPORTED: | ||
160 | + trace_uefi_status(action, "unsupported"); | ||
161 | + break; | ||
162 | + case EFI_BAD_BUFFER_SIZE: | ||
163 | + trace_uefi_status(action, "bad buffer size"); | ||
164 | + break; | ||
165 | + case EFI_BUFFER_TOO_SMALL: | ||
166 | + trace_uefi_status(action, "buffer too small"); | ||
167 | + break; | ||
168 | + case EFI_WRITE_PROTECTED: | ||
169 | + trace_uefi_status(action, "write protected"); | ||
170 | + break; | ||
171 | + case EFI_OUT_OF_RESOURCES: | ||
172 | + trace_uefi_status(action, "out of resources"); | ||
173 | + break; | ||
174 | + case EFI_NOT_FOUND: | ||
175 | + trace_uefi_status(action, "not found"); | ||
176 | + break; | ||
177 | + case EFI_ACCESS_DENIED: | ||
178 | + trace_uefi_status(action, "access denied"); | ||
179 | + break; | ||
180 | + case EFI_ALREADY_STARTED: | ||
181 | + trace_uefi_status(action, "already started"); | ||
182 | + break; | ||
183 | + default: | ||
184 | + trace_uefi_status(action, "unknown error"); | ||
185 | + break; | ||
186 | + } | ||
187 | +} | ||
188 | + | ||
189 | +static uint32_t uefi_vars_cmd_mm(uefi_vars_state *uv) | 73 | +static uint32_t uefi_vars_cmd_mm(uefi_vars_state *uv) |
190 | +{ | 74 | +{ |
191 | + hwaddr dma; | 75 | + hwaddr dma; |
192 | + mm_header *mhdr; | 76 | + mm_header *mhdr; |
193 | + uint32_t size, retval; | 77 | + uint64_t size; |
78 | + uint32_t retval; | ||
194 | + | 79 | + |
195 | + dma = uv->buf_addr_lo | ((hwaddr)uv->buf_addr_hi << 32); | 80 | + dma = uv->buf_addr_lo | ((hwaddr)uv->buf_addr_hi << 32); |
196 | + mhdr = (mm_header *) uv->buffer; | 81 | + mhdr = (mm_header *) uv->buffer; |
197 | + | 82 | + |
198 | + if (!uv->buffer || uv->buf_size < sizeof(*mhdr)) { | 83 | + if (!uv->buffer || uv->buf_size < sizeof(*mhdr)) { |
... | ... | ||
202 | + /* read header */ | 87 | + /* read header */ |
203 | + dma_memory_read(&address_space_memory, dma, | 88 | + dma_memory_read(&address_space_memory, dma, |
204 | + uv->buffer, sizeof(*mhdr), | 89 | + uv->buffer, sizeof(*mhdr), |
205 | + MEMTXATTRS_UNSPECIFIED); | 90 | + MEMTXATTRS_UNSPECIFIED); |
206 | + | 91 | + |
207 | + size = sizeof(*mhdr) + mhdr->length; | 92 | + if (uadd64_overflow(sizeof(*mhdr), mhdr->length, &size)) { |
93 | + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; | ||
94 | + } | ||
208 | + if (uv->buf_size < size) { | 95 | + if (uv->buf_size < size) { |
209 | + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; | 96 | + return UEFI_VARS_STS_ERR_BAD_BUFFER_SIZE; |
210 | + } | 97 | + } |
211 | + | 98 | + |
212 | + /* read buffer (excl header) */ | 99 | + /* read buffer (excl header) */ |
... | ... | ||
363 | +{ | 250 | +{ |
364 | + uefi_vars_json_init(uv, errp); | 251 | + uefi_vars_json_init(uv, errp); |
365 | + uefi_vars_json_load(uv, errp); | 252 | + uefi_vars_json_load(uv, errp); |
366 | +} | 253 | +} |
367 | -- | 254 | -- |
368 | 2.41.0 | 255 | 2.47.1 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | This implements pkcs7 signature verification using gnutls. | ||
2 | Needed to check authenticated variable updates. | ||
1 | 3 | ||
4 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | ||
5 | --- | ||
6 | hw/uefi/var-service-pkcs7.c | 436 ++++++++++++++++++++++++++++++++++++ | ||
7 | 1 file changed, 436 insertions(+) | ||
8 | create mode 100644 hw/uefi/var-service-pkcs7.c | ||
9 | |||
10 | diff --git a/hw/uefi/var-service-pkcs7.c b/hw/uefi/var-service-pkcs7.c | ||
11 | new file mode 100644 | ||
12 | index XXXXXXX..XXXXXXX | ||
13 | --- /dev/null | ||
14 | +++ b/hw/uefi/var-service-pkcs7.c | ||
15 | @@ -XXX,XX +XXX,XX @@ | ||
16 | +/* | ||
17 | + * SPDX-License-Identifier: GPL-2.0-or-later | ||
18 | + * | ||
19 | + * uefi vars device - pkcs7 verification | ||
20 | + */ | ||
21 | +#include "qemu/osdep.h" | ||
22 | +#include "qemu/error-report.h" | ||
23 | +#include "system/dma.h" | ||
24 | + | ||
25 | +#include <gnutls/gnutls.h> | ||
26 | +#include <gnutls/pkcs7.h> | ||
27 | +#include <gnutls/crypto.h> | ||
28 | + | ||
29 | +#include "hw/uefi/var-service.h" | ||
30 | + | ||
31 | +#define AUTHVAR_DIGEST_ALGO GNUTLS_DIG_SHA256 | ||
32 | +#define AUTHVAR_DIGEST_SIZE 32 | ||
33 | + | ||
34 | +/* | ||
35 | + * Replicate the signed data for signature verification. | ||
36 | + */ | ||
37 | +static gnutls_datum_t *build_signed_data(mm_variable_access *va, void *data) | ||
38 | +{ | ||
39 | + variable_auth_2 *auth = data; | ||
40 | + uint64_t data_offset = sizeof(efi_time) + auth->hdr_length; | ||
41 | + uint16_t *name = (void *)va + sizeof(mm_variable_access); | ||
42 | + gnutls_datum_t *sdata; | ||
43 | + uint64_t pos = 0; | ||
44 | + | ||
45 | + sdata = g_new(gnutls_datum_t, 1); | ||
46 | + sdata->size = (va->name_size - 2 | ||
47 | + + sizeof(QemuUUID) | ||
48 | + + sizeof(va->attributes) | ||
49 | + + sizeof(auth->timestamp) | ||
50 | + + va->data_size - data_offset); | ||
51 | + sdata->data = g_malloc(sdata->size); | ||
52 | + | ||
53 | + /* Variable Name (without terminating \0) */ | ||
54 | + memcpy(sdata->data + pos, name, va->name_size - 2); | ||
55 | + pos += va->name_size - 2; | ||
56 | + | ||
57 | + /* Variable Namespace Guid */ | ||
58 | + memcpy(sdata->data + pos, &va->guid, sizeof(va->guid)); | ||
59 | + pos += sizeof(va->guid); | ||
60 | + | ||
61 | + /* Attributes */ | ||
62 | + memcpy(sdata->data + pos, &va->attributes, sizeof(va->attributes)); | ||
63 | + pos += sizeof(va->attributes); | ||
64 | + | ||
65 | + /* TimeStamp */ | ||
66 | + memcpy(sdata->data + pos, &auth->timestamp, sizeof(auth->timestamp)); | ||
67 | + pos += sizeof(auth->timestamp); | ||
68 | + | ||
69 | + /* Variable Content */ | ||
70 | + memcpy(sdata->data + pos, data + data_offset, va->data_size - data_offset); | ||
71 | + pos += va->data_size - data_offset; | ||
72 | + | ||
73 | + assert(pos == sdata->size); | ||
74 | + return sdata; | ||
75 | +} | ||
76 | + | ||
77 | +/* | ||
78 | + * See WrapPkcs7Data() in edk2. | ||
79 | + * | ||
80 | + * UEFI spec allows pkcs7 signatures being used without the envelope which | ||
81 | + * identifies them as pkcs7 signatures. openssl and gnutls will not parse them | ||
82 | + * without the envelope though. So add it if needed. | ||
83 | + */ | ||
84 | +static void wrap_pkcs7(gnutls_datum_t *pkcs7) | ||
85 | +{ | ||
86 | + static uint8_t signed_data_oid[9] = { | ||
87 | + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02 | ||
88 | + }; | ||
89 | + gnutls_datum_t wrap; | ||
90 | + | ||
91 | + if (pkcs7->data[4] == 0x06 && | ||
92 | + pkcs7->data[5] == 0x09 && | ||
93 | + memcmp(pkcs7->data + 6, signed_data_oid, sizeof(signed_data_oid)) == 0 && | ||
94 | + pkcs7->data[15] == 0x0a && | ||
95 | + pkcs7->data[16] == 0x82) { | ||
96 | + return; | ||
97 | + } | ||
98 | + | ||
99 | + wrap.size = pkcs7->size + 19; | ||
100 | + wrap.data = g_malloc(wrap.size); | ||
101 | + | ||
102 | + wrap.data[0] = 0x30; | ||
103 | + wrap.data[1] = 0x82; | ||
104 | + wrap.data[2] = (wrap.size - 4) >> 8; | ||
105 | + wrap.data[3] = (wrap.size - 4) & 0xff; | ||
106 | + wrap.data[4] = 0x06; | ||
107 | + wrap.data[5] = 0x09; | ||
108 | + memcpy(wrap.data + 6, signed_data_oid, sizeof(signed_data_oid)); | ||
109 | + | ||
110 | + wrap.data[15] = 0xa0; | ||
111 | + wrap.data[16] = 0x82; | ||
112 | + wrap.data[17] = pkcs7->size >> 8; | ||
113 | + wrap.data[18] = pkcs7->size & 0xff; | ||
114 | + memcpy(wrap.data + 19, pkcs7->data, pkcs7->size); | ||
115 | + | ||
116 | + g_free(pkcs7->data); | ||
117 | + *pkcs7 = wrap; | ||
118 | +} | ||
119 | + | ||
120 | +static gnutls_datum_t *build_pkcs7(void *data) | ||
121 | +{ | ||
122 | + variable_auth_2 *auth = data; | ||
123 | + gnutls_datum_t *pkcs7; | ||
124 | + | ||
125 | + pkcs7 = g_new(gnutls_datum_t, 1); | ||
126 | + pkcs7->size = auth->hdr_length - 24; | ||
127 | + pkcs7->data = g_malloc(pkcs7->size); | ||
128 | + memcpy(pkcs7->data, data + 16 + 24, pkcs7->size); | ||
129 | + | ||
130 | + wrap_pkcs7(pkcs7); | ||
131 | + | ||
132 | + return pkcs7; | ||
133 | +} | ||
134 | + | ||
135 | +/* | ||
136 | + * Read UEFI signature database, store x509 all certificates found in | ||
137 | + * gnutls_x509_trust_list_t. | ||
138 | + */ | ||
139 | +static gnutls_x509_trust_list_t build_trust_list_sb(uefi_variable *var) | ||
140 | +{ | ||
141 | + gnutls_x509_trust_list_t tlist; | ||
142 | + gnutls_datum_t cert_data; | ||
143 | + gnutls_x509_crt_t cert; | ||
144 | + uefi_vars_siglist siglist; | ||
145 | + uefi_vars_cert *c; | ||
146 | + int rc; | ||
147 | + | ||
148 | + rc = gnutls_x509_trust_list_init(&tlist, 0); | ||
149 | + if (rc < 0) { | ||
150 | + warn_report("gnutls_x509_trust_list_init error: %s", | ||
151 | + gnutls_strerror(rc)); | ||
152 | + return NULL; | ||
153 | + } | ||
154 | + | ||
155 | + uefi_vars_siglist_init(&siglist); | ||
156 | + uefi_vars_siglist_parse(&siglist, var->data, var->data_size); | ||
157 | + | ||
158 | + QTAILQ_FOREACH(c, &siglist.x509, next) { | ||
159 | + cert_data.size = c->size; | ||
160 | + cert_data.data = c->data; | ||
161 | + | ||
162 | + rc = gnutls_x509_crt_init(&cert); | ||
163 | + if (rc < 0) { | ||
164 | + warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc)); | ||
165 | + break; | ||
166 | + } | ||
167 | + rc = gnutls_x509_crt_import(cert, &cert_data, GNUTLS_X509_FMT_DER); | ||
168 | + if (rc < 0) { | ||
169 | + warn_report("gnutls_x509_crt_import error: %s", | ||
170 | + gnutls_strerror(rc)); | ||
171 | + gnutls_x509_crt_deinit(cert); | ||
172 | + break; | ||
173 | + } | ||
174 | + rc = gnutls_x509_trust_list_add_cas(tlist, &cert, 1, 0); | ||
175 | + if (rc < 0) { | ||
176 | + warn_report("gnutls_x509_crt_import error: %s", | ||
177 | + gnutls_strerror(rc)); | ||
178 | + gnutls_x509_crt_deinit(cert); | ||
179 | + break; | ||
180 | + } | ||
181 | + } | ||
182 | + | ||
183 | + uefi_vars_siglist_free(&siglist); | ||
184 | + | ||
185 | + return tlist; | ||
186 | +} | ||
187 | + | ||
188 | +static int build_digest_authvar(gnutls_x509_crt_t signer, | ||
189 | + gnutls_x509_crt_t root, | ||
190 | + uint8_t *hash_digest) | ||
191 | +{ | ||
192 | + char *cn; | ||
193 | + size_t cn_size = 0; | ||
194 | + uint8_t fp[AUTHVAR_DIGEST_SIZE]; | ||
195 | + size_t fp_size = sizeof(fp); | ||
196 | + gnutls_hash_hd_t hash; | ||
197 | + int rc; | ||
198 | + | ||
199 | + /* get signer CN */ | ||
200 | + rc = gnutls_x509_crt_get_dn_by_oid(signer, GNUTLS_OID_X520_COMMON_NAME, | ||
201 | + 0, 0, NULL, &cn_size); | ||
202 | + if (rc != GNUTLS_E_SHORT_MEMORY_BUFFER) { | ||
203 | + warn_report("gnutls_x509_crt_get_dn_by_oid error #1: %s", | ||
204 | + gnutls_strerror(rc)); | ||
205 | + return rc; | ||
206 | + } | ||
207 | + | ||
208 | + cn = g_malloc(cn_size); | ||
209 | + rc = gnutls_x509_crt_get_dn_by_oid(signer, GNUTLS_OID_X520_COMMON_NAME, | ||
210 | + 0, 0, cn, &cn_size); | ||
211 | + if (rc < 0) { | ||
212 | + warn_report("gnutls_x509_crt_get_dn_by_oid error #2: %s", | ||
213 | + gnutls_strerror(rc)); | ||
214 | + goto err; | ||
215 | + } | ||
216 | + | ||
217 | + /* get root certificate fingerprint */ | ||
218 | + rc = gnutls_x509_crt_get_fingerprint(root, AUTHVAR_DIGEST_ALGO, | ||
219 | + fp, &fp_size); | ||
220 | + if (rc < 0) { | ||
221 | + warn_report("gnutls_x509_crt_get_fingerprint error: %s", | ||
222 | + gnutls_strerror(rc)); | ||
223 | + goto err; | ||
224 | + } | ||
225 | + | ||
226 | + /* digest both items */ | ||
227 | + rc = gnutls_hash_init(&hash, AUTHVAR_DIGEST_ALGO); | ||
228 | + if (rc < 0) { | ||
229 | + warn_report("gnutls_hash_init error: %s", | ||
230 | + gnutls_strerror(rc)); | ||
231 | + goto err; | ||
232 | + } | ||
233 | + rc = gnutls_hash(hash, cn, cn_size); | ||
234 | + if (rc < 0) { | ||
235 | + warn_report("gnutls_hash error: %s", | ||
236 | + gnutls_strerror(rc)); | ||
237 | + goto err; | ||
238 | + } | ||
239 | + rc = gnutls_hash(hash, fp, fp_size); | ||
240 | + if (rc < 0) { | ||
241 | + warn_report("gnutls_hash error: %s", | ||
242 | + gnutls_strerror(rc)); | ||
243 | + goto err; | ||
244 | + } | ||
245 | + gnutls_hash_deinit(hash, hash_digest); | ||
246 | + | ||
247 | + return 0; | ||
248 | + | ||
249 | +err: | ||
250 | + g_free(cn); | ||
251 | + return rc; | ||
252 | +} | ||
253 | + | ||
254 | +/* | ||
255 | + * uefi spec 2.9, section 8.2.2 | ||
256 | + * | ||
257 | + * For EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS variables which are | ||
258 | + * NOT secure boot variables we should track the root certificate of the trust | ||
259 | + * chain, and the subject CN of the signer certificate. | ||
260 | + * | ||
261 | + * So we'll go store a digest of these two items so we can verify this. Also | ||
262 | + * create a gnutls_x509_trust_list_t with the root certificate, so | ||
263 | + * gnutls_pkcs7_verify() will pass (assuming the signature is otherwise | ||
264 | + * correct). | ||
265 | + */ | ||
266 | +static gnutls_x509_trust_list_t build_trust_list_authvar(gnutls_pkcs7_t pkcs7, | ||
267 | + uint8_t *hash_digest) | ||
268 | +{ | ||
269 | + gnutls_datum_t signer_data = { 0 }; | ||
270 | + gnutls_datum_t root_data = { 0 }; | ||
271 | + gnutls_x509_crt_t signer = NULL; | ||
272 | + gnutls_x509_crt_t root = NULL; | ||
273 | + gnutls_x509_trust_list_t tlist = NULL; | ||
274 | + int n, rc; | ||
275 | + | ||
276 | + n = gnutls_pkcs7_get_crt_count(pkcs7); | ||
277 | + | ||
278 | + /* first is signer certificate */ | ||
279 | + rc = gnutls_pkcs7_get_crt_raw2(pkcs7, 0, &signer_data); | ||
280 | + if (rc < 0) { | ||
281 | + warn_report("gnutls_pkcs7_get_crt_raw2(0) error: %s", | ||
282 | + gnutls_strerror(rc)); | ||
283 | + goto done; | ||
284 | + } | ||
285 | + rc = gnutls_x509_crt_init(&signer); | ||
286 | + if (rc < 0) { | ||
287 | + warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc)); | ||
288 | + goto done; | ||
289 | + } | ||
290 | + rc = gnutls_x509_crt_import(signer, &signer_data, GNUTLS_X509_FMT_DER); | ||
291 | + if (rc < 0) { | ||
292 | + warn_report("gnutls_x509_crt_import error: %s", | ||
293 | + gnutls_strerror(rc)); | ||
294 | + gnutls_x509_crt_deinit(signer); | ||
295 | + goto done; | ||
296 | + } | ||
297 | + | ||
298 | + /* last is root-of-trust certificate (can be identical to signer) */ | ||
299 | + rc = gnutls_pkcs7_get_crt_raw2(pkcs7, n - 1, &root_data); | ||
300 | + if (rc < 0) { | ||
301 | + warn_report("gnutls_pkcs7_get_crt_raw2(%d) error: %s", | ||
302 | + n - 1, gnutls_strerror(rc)); | ||
303 | + goto done; | ||
304 | + } | ||
305 | + rc = gnutls_x509_crt_init(&root); | ||
306 | + if (rc < 0) { | ||
307 | + warn_report("gnutls_x509_crt_init error: %s", gnutls_strerror(rc)); | ||
308 | + goto done; | ||
309 | + } | ||
310 | + rc = gnutls_x509_crt_import(root, &root_data, GNUTLS_X509_FMT_DER); | ||
311 | + if (rc < 0) { | ||
312 | + warn_report("gnutls_x509_crt_import error: %s", | ||
313 | + gnutls_strerror(rc)); | ||
314 | + goto done; | ||
315 | + } | ||
316 | + | ||
317 | + /* calc digest for signer CN + root cert */ | ||
318 | + rc = build_digest_authvar(signer, root, hash_digest); | ||
319 | + if (rc < 0) { | ||
320 | + goto done; | ||
321 | + } | ||
322 | + | ||
323 | + /* add root to trust list */ | ||
324 | + rc = gnutls_x509_trust_list_init(&tlist, 0); | ||
325 | + if (rc < 0) { | ||
326 | + warn_report("gnutls_x509_trust_list_init error: %s", | ||
327 | + gnutls_strerror(rc)); | ||
328 | + goto done; | ||
329 | + } | ||
330 | + rc = gnutls_x509_trust_list_add_cas(tlist, &root, 1, 0); | ||
331 | + if (rc < 0) { | ||
332 | + warn_report("gnutls_x509_crt_import error: %s", | ||
333 | + gnutls_strerror(rc)); | ||
334 | + gnutls_x509_trust_list_deinit(tlist, 1); | ||
335 | + tlist = NULL; | ||
336 | + goto done; | ||
337 | + } else { | ||
338 | + /* ownership passed to tlist */ | ||
339 | + root = NULL; | ||
340 | + } | ||
341 | + | ||
342 | +done: | ||
343 | + if (signer_data.data) { | ||
344 | + gnutls_free(signer_data.data); | ||
345 | + } | ||
346 | + if (root_data.data) { | ||
347 | + gnutls_free(root_data.data); | ||
348 | + } | ||
349 | + if (signer) { | ||
350 | + gnutls_x509_crt_deinit(signer); | ||
351 | + } | ||
352 | + if (root) { | ||
353 | + gnutls_x509_crt_deinit(root); | ||
354 | + } | ||
355 | + return tlist; | ||
356 | +} | ||
357 | + | ||
358 | +static void free_datum(gnutls_datum_t *ptr) | ||
359 | +{ | ||
360 | + if (!ptr) { | ||
361 | + return; | ||
362 | + } | ||
363 | + g_free(ptr->data); | ||
364 | + g_free(ptr); | ||
365 | +} | ||
366 | + | ||
367 | +static void gnutls_log_stderr(int level, const char *msg) | ||
368 | +{ | ||
369 | + if (strncmp(msg, "ASSERT:", 7) == 0) { | ||
370 | + return; | ||
371 | + } | ||
372 | + fprintf(stderr, " %d: %s", level, msg); | ||
373 | +} | ||
374 | + | ||
375 | +/* | ||
376 | + * pkcs7 signature verification (EFI_VARIABLE_AUTHENTICATION_2). | ||
377 | + */ | ||
378 | +efi_status uefi_vars_check_pkcs7_2(uefi_variable *siglist, | ||
379 | + void **digest, uint32_t *digest_size, | ||
380 | + mm_variable_access *va, void *data) | ||
381 | +{ | ||
382 | + gnutls_x509_trust_list_t tlist = NULL; | ||
383 | + gnutls_datum_t *signed_data = NULL; | ||
384 | + gnutls_datum_t *pkcs7_data = NULL; | ||
385 | + gnutls_pkcs7_t pkcs7 = NULL; | ||
386 | + efi_status status = EFI_SECURITY_VIOLATION; | ||
387 | + int rc; | ||
388 | + | ||
389 | + if (0) { | ||
390 | + /* gnutls debug logging */ | ||
391 | + static bool first = true; | ||
392 | + | ||
393 | + if (first) { | ||
394 | + first = false; | ||
395 | + gnutls_global_set_log_function(gnutls_log_stderr); | ||
396 | + gnutls_global_set_log_level(99); | ||
397 | + } | ||
398 | + } | ||
399 | + | ||
400 | + signed_data = build_signed_data(va, data); | ||
401 | + pkcs7_data = build_pkcs7(data); | ||
402 | + | ||
403 | + rc = gnutls_pkcs7_init(&pkcs7); | ||
404 | + if (rc < 0) { | ||
405 | + warn_report("gnutls_pkcs7_init error: %s", gnutls_strerror(rc)); | ||
406 | + goto out; | ||
407 | + } | ||
408 | + | ||
409 | + rc = gnutls_pkcs7_import(pkcs7, pkcs7_data, GNUTLS_X509_FMT_DER); | ||
410 | + if (rc < 0) { | ||
411 | + warn_report("gnutls_pkcs7_import error: %s", gnutls_strerror(rc)); | ||
412 | + goto out; | ||
413 | + } | ||
414 | + | ||
415 | + if (siglist) { | ||
416 | + /* secure boot variables */ | ||
417 | + tlist = build_trust_list_sb(siglist); | ||
418 | + } else if (digest && digest_size) { | ||
419 | + /* other authenticated variables */ | ||
420 | + *digest_size = AUTHVAR_DIGEST_SIZE; | ||
421 | + *digest = g_malloc(*digest_size); | ||
422 | + tlist = build_trust_list_authvar(pkcs7, *digest); | ||
423 | + } else { | ||
424 | + /* should not happen */ | ||
425 | + goto out; | ||
426 | + } | ||
427 | + | ||
428 | + rc = gnutls_pkcs7_verify(pkcs7, tlist, | ||
429 | + NULL, 0, | ||
430 | + 0, signed_data, | ||
431 | + GNUTLS_VERIFY_DISABLE_TIME_CHECKS | | ||
432 | + GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS); | ||
433 | + if (rc < 0) { | ||
434 | + warn_report("gnutls_pkcs7_verify error: %s", gnutls_strerror(rc)); | ||
435 | + goto out; | ||
436 | + } | ||
437 | + | ||
438 | + /* check passed */ | ||
439 | + status = EFI_SUCCESS; | ||
440 | + | ||
441 | +out: | ||
442 | + free_datum(signed_data); | ||
443 | + free_datum(pkcs7_data); | ||
444 | + if (tlist) { | ||
445 | + gnutls_x509_trust_list_deinit(tlist, 1); | ||
446 | + } | ||
447 | + if (pkcs7) { | ||
448 | + gnutls_pkcs7_deinit(pkcs7); | ||
449 | + } | ||
450 | + return status; | ||
451 | +} | ||
452 | -- | ||
453 | 2.47.1 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | pkcs7 stub which is used in case gnutls is not available. | ||
1 | 2 | ||
3 | It throws EFI_WRITE_PROTECTED errors unconditionally, so all | ||
4 | authenticated variables are readonly for the guest. | ||
5 | |||
6 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | ||
7 | --- | ||
8 | hw/uefi/var-service-pkcs7-stub.c | 16 ++++++++++++++++ | ||
9 | 1 file changed, 16 insertions(+) | ||
10 | create mode 100644 hw/uefi/var-service-pkcs7-stub.c | ||
11 | |||
12 | diff --git a/hw/uefi/var-service-pkcs7-stub.c b/hw/uefi/var-service-pkcs7-stub.c | ||
13 | new file mode 100644 | ||
14 | index XXXXXXX..XXXXXXX | ||
15 | --- /dev/null | ||
16 | +++ b/hw/uefi/var-service-pkcs7-stub.c | ||
17 | @@ -XXX,XX +XXX,XX @@ | ||
18 | +/* | ||
19 | + * SPDX-License-Identifier: GPL-2.0-or-later | ||
20 | + * | ||
21 | + * uefi vars device - pkcs7 stubs | ||
22 | + */ | ||
23 | +#include "qemu/osdep.h" | ||
24 | +#include "system/dma.h" | ||
25 | + | ||
26 | +#include "hw/uefi/var-service.h" | ||
27 | + | ||
28 | +efi_status uefi_vars_check_pkcs7_2(uefi_variable *siglist, | ||
29 | + void **digest, uint32_t *digest_size, | ||
30 | + mm_variable_access *va, void *data) | ||
31 | +{ | ||
32 | + return EFI_WRITE_PROTECTED; | ||
33 | +} | ||
34 | -- | ||
35 | 2.47.1 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | Functions to serialize and de-serialize EFI signature databases. This | ||
2 | is needed to merge signature databases (happens in practice when | ||
3 | appending dbx updates) and also to extract the certificates for | ||
4 | pkcs7 signature verification. | ||
1 | 5 | ||
6 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | ||
7 | --- | ||
8 | hw/uefi/var-service-siglist.c | 212 ++++++++++++++++++++++++++++++++++ | ||
9 | 1 file changed, 212 insertions(+) | ||
10 | create mode 100644 hw/uefi/var-service-siglist.c | ||
11 | |||
12 | diff --git a/hw/uefi/var-service-siglist.c b/hw/uefi/var-service-siglist.c | ||
13 | new file mode 100644 | ||
14 | index XXXXXXX..XXXXXXX | ||
15 | --- /dev/null | ||
16 | +++ b/hw/uefi/var-service-siglist.c | ||
17 | @@ -XXX,XX +XXX,XX @@ | ||
18 | +/* | ||
19 | + * SPDX-License-Identifier: GPL-2.0-or-later | ||
20 | + * | ||
21 | + * uefi vars device - parse and generate efi signature databases | ||
22 | + */ | ||
23 | + | ||
24 | +#include "qemu/osdep.h" | ||
25 | +#include "qemu/error-report.h" | ||
26 | +#include "system/dma.h" | ||
27 | + | ||
28 | +#include "hw/uefi/var-service.h" | ||
29 | + | ||
30 | +/* | ||
31 | + * Add x509 certificate to list (with duplicate check). | ||
32 | + */ | ||
33 | +static void uefi_vars_siglist_add_x509(uefi_vars_siglist *siglist, | ||
34 | + QemuUUID *owner, | ||
35 | + void *data, uint64_t size) | ||
36 | +{ | ||
37 | + uefi_vars_cert *c; | ||
38 | + | ||
39 | + QTAILQ_FOREACH(c, &siglist->x509, next) { | ||
40 | + if (c->size != size) { | ||
41 | + continue; | ||
42 | + } | ||
43 | + if (memcmp(c->data, data, size) != 0) { | ||
44 | + continue; | ||
45 | + } | ||
46 | + return; | ||
47 | + } | ||
48 | + | ||
49 | + c = g_malloc(sizeof(*c) + size); | ||
50 | + c->owner = *owner; | ||
51 | + c->size = size; | ||
52 | + memcpy(c->data, data, size); | ||
53 | + QTAILQ_INSERT_TAIL(&siglist->x509, c, next); | ||
54 | +} | ||
55 | + | ||
56 | +/* | ||
57 | + * Add sha256 hash to list (with duplicate check). | ||
58 | + */ | ||
59 | +static void uefi_vars_siglist_add_sha256(uefi_vars_siglist *siglist, | ||
60 | + QemuUUID *owner, | ||
61 | + void *data) | ||
62 | +{ | ||
63 | + uefi_vars_hash *h; | ||
64 | + | ||
65 | + QTAILQ_FOREACH(h, &siglist->sha256, next) { | ||
66 | + if (memcmp(h->data, data, 32) != 0) { | ||
67 | + continue; | ||
68 | + } | ||
69 | + return; | ||
70 | + } | ||
71 | + | ||
72 | + h = g_malloc(sizeof(*h) + 32); | ||
73 | + h->owner = *owner; | ||
74 | + memcpy(h->data, data, 32); | ||
75 | + QTAILQ_INSERT_TAIL(&siglist->sha256, h, next); | ||
76 | +} | ||
77 | + | ||
78 | +void uefi_vars_siglist_init(uefi_vars_siglist *siglist) | ||
79 | +{ | ||
80 | + memset(siglist, 0, sizeof(*siglist)); | ||
81 | + QTAILQ_INIT(&siglist->x509); | ||
82 | + QTAILQ_INIT(&siglist->sha256); | ||
83 | +} | ||
84 | + | ||
85 | +void uefi_vars_siglist_free(uefi_vars_siglist *siglist) | ||
86 | +{ | ||
87 | + uefi_vars_cert *c, *cs; | ||
88 | + uefi_vars_hash *h, *hs; | ||
89 | + | ||
90 | + QTAILQ_FOREACH_SAFE(c, &siglist->x509, next, cs) { | ||
91 | + QTAILQ_REMOVE(&siglist->x509, c, next); | ||
92 | + g_free(c); | ||
93 | + } | ||
94 | + QTAILQ_FOREACH_SAFE(h, &siglist->sha256, next, hs) { | ||
95 | + QTAILQ_REMOVE(&siglist->sha256, h, next); | ||
96 | + g_free(h); | ||
97 | + } | ||
98 | +} | ||
99 | + | ||
100 | +/* | ||
101 | + * Parse UEFI signature list. | ||
102 | + */ | ||
103 | +void uefi_vars_siglist_parse(uefi_vars_siglist *siglist, | ||
104 | + void *data, uint64_t size) | ||
105 | +{ | ||
106 | + efi_siglist *efilist; | ||
107 | + uint64_t start; | ||
108 | + | ||
109 | + while (size) { | ||
110 | + if (size < sizeof(*efilist)) { | ||
111 | + break; | ||
112 | + } | ||
113 | + efilist = data; | ||
114 | + if (size < efilist->siglist_size) { | ||
115 | + break; | ||
116 | + } | ||
117 | + | ||
118 | + if (uadd64_overflow(sizeof(*efilist), efilist->header_size, &start)) { | ||
119 | + break; | ||
120 | + } | ||
121 | + if (efilist->sig_size <= sizeof(QemuUUID)) { | ||
122 | + break; | ||
123 | + } | ||
124 | + | ||
125 | + if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertX509Guid)) { | ||
126 | + if (start + efilist->sig_size != efilist->siglist_size) { | ||
127 | + break; | ||
128 | + } | ||
129 | + uefi_vars_siglist_add_x509(siglist, | ||
130 | + (QemuUUID *)(data + start), | ||
131 | + data + start + sizeof(QemuUUID), | ||
132 | + efilist->sig_size - sizeof(QemuUUID)); | ||
133 | + | ||
134 | + } else if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertSha256Guid)) { | ||
135 | + if (efilist->sig_size != sizeof(QemuUUID) + 32) { | ||
136 | + break; | ||
137 | + } | ||
138 | + if (start + efilist->sig_size > efilist->siglist_size) { | ||
139 | + break; | ||
140 | + } | ||
141 | + while (start <= efilist->siglist_size - efilist->sig_size) { | ||
142 | + uefi_vars_siglist_add_sha256(siglist, | ||
143 | + (QemuUUID *)(data + start), | ||
144 | + data + start + sizeof(QemuUUID)); | ||
145 | + start += efilist->sig_size; | ||
146 | + } | ||
147 | + | ||
148 | + } else { | ||
149 | + QemuUUID be = qemu_uuid_bswap(efilist->guid_type); | ||
150 | + char *str_uuid = qemu_uuid_unparse_strdup(&be); | ||
151 | + warn_report("%s: unknown type (%s)", __func__, str_uuid); | ||
152 | + g_free(str_uuid); | ||
153 | + } | ||
154 | + | ||
155 | + data += efilist->siglist_size; | ||
156 | + size -= efilist->siglist_size; | ||
157 | + } | ||
158 | +} | ||
159 | + | ||
160 | +uint64_t uefi_vars_siglist_blob_size(uefi_vars_siglist *siglist) | ||
161 | +{ | ||
162 | + uefi_vars_cert *c; | ||
163 | + uefi_vars_hash *h; | ||
164 | + uint64_t size = 0; | ||
165 | + | ||
166 | + QTAILQ_FOREACH(c, &siglist->x509, next) { | ||
167 | + size += sizeof(efi_siglist) + sizeof(QemuUUID) + c->size; | ||
168 | + } | ||
169 | + | ||
170 | + if (!QTAILQ_EMPTY(&siglist->sha256)) { | ||
171 | + size += sizeof(efi_siglist); | ||
172 | + QTAILQ_FOREACH(h, &siglist->sha256, next) { | ||
173 | + size += sizeof(QemuUUID) + 32; | ||
174 | + } | ||
175 | + } | ||
176 | + | ||
177 | + return size; | ||
178 | +} | ||
179 | + | ||
180 | +/* | ||
181 | + * Generate UEFI signature list. | ||
182 | + */ | ||
183 | +void uefi_vars_siglist_blob_generate(uefi_vars_siglist *siglist, | ||
184 | + void *data, uint64_t size) | ||
185 | +{ | ||
186 | + uefi_vars_cert *c; | ||
187 | + uefi_vars_hash *h; | ||
188 | + efi_siglist *efilist; | ||
189 | + uint64_t pos = 0, start; | ||
190 | + uint32_t i; | ||
191 | + | ||
192 | + QTAILQ_FOREACH(c, &siglist->x509, next) { | ||
193 | + efilist = data + pos; | ||
194 | + efilist->guid_type = EfiCertX509Guid; | ||
195 | + efilist->sig_size = sizeof(QemuUUID) + c->size; | ||
196 | + efilist->header_size = 0; | ||
197 | + | ||
198 | + start = pos + sizeof(efi_siglist); | ||
199 | + memcpy(data + start, | ||
200 | + &c->owner, sizeof(QemuUUID)); | ||
201 | + memcpy(data + start + sizeof(QemuUUID), | ||
202 | + c->data, c->size); | ||
203 | + | ||
204 | + efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size; | ||
205 | + pos += efilist->siglist_size; | ||
206 | + } | ||
207 | + | ||
208 | + if (!QTAILQ_EMPTY(&siglist->sha256)) { | ||
209 | + efilist = data + pos; | ||
210 | + efilist->guid_type = EfiCertSha256Guid; | ||
211 | + efilist->sig_size = sizeof(QemuUUID) + 32; | ||
212 | + efilist->header_size = 0; | ||
213 | + | ||
214 | + i = 0; | ||
215 | + start = pos + sizeof(efi_siglist); | ||
216 | + QTAILQ_FOREACH(h, &siglist->sha256, next) { | ||
217 | + memcpy(data + start + efilist->sig_size * i, | ||
218 | + &h->owner, sizeof(QemuUUID)); | ||
219 | + memcpy(data + start + efilist->sig_size * i + sizeof(QemuUUID), | ||
220 | + h->data, 32); | ||
221 | + i++; | ||
222 | + } | ||
223 | + | ||
224 | + efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size * i; | ||
225 | + pos += efilist->siglist_size; | ||
226 | + } | ||
227 | + | ||
228 | + assert(pos == size); | ||
229 | +} | ||
230 | -- | ||
231 | 2.47.1 | diff view generated by jsdifflib |
1 | Define qapi schema for the uefi variable store state. | 1 | Define qapi schema for the uefi variable store state. |
---|---|---|---|
2 | 2 | ||
3 | Use it and the generated visitor helper functions to | 3 | Use it and the generated visitor helper functions to store persistent |
4 | store persistent variables in JSON format on disk. | 4 | (EFI_VARIABLE_NON_VOLATILE) variables in JSON format on disk. |
5 | 5 | ||
6 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 6 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
7 | --- | 7 | --- |
8 | hw/uefi/var-service-json.c | 194 +++++++++++++++++++++++++++++++++++++ | 8 | hw/uefi/var-service-json.c | 242 +++++++++++++++++++++++++++++++++++++ |
9 | qapi/meson.build | 1 + | 9 | qapi/meson.build | 1 + |
10 | qapi/qapi-schema.json | 1 + | 10 | qapi/qapi-schema.json | 1 + |
11 | qapi/uefi.json | 40 ++++++++ | 11 | qapi/uefi.json | 45 +++++++ |
12 | 4 files changed, 236 insertions(+) | 12 | 4 files changed, 289 insertions(+) |
13 | create mode 100644 hw/uefi/var-service-json.c | 13 | create mode 100644 hw/uefi/var-service-json.c |
14 | create mode 100644 qapi/uefi.json | 14 | create mode 100644 qapi/uefi.json |
15 | 15 | ||
16 | diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c | 16 | diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c |
17 | new file mode 100644 | 17 | new file mode 100644 |
... | ... | ||
29 | + * - https://gitlab.com/kraxel/virt-firmware | 29 | + * - https://gitlab.com/kraxel/virt-firmware |
30 | + * - https://github.com/awslabs/python-uefivars | 30 | + * - https://github.com/awslabs/python-uefivars |
31 | + */ | 31 | + */ |
32 | +#include "qemu/osdep.h" | 32 | +#include "qemu/osdep.h" |
33 | +#include "qemu/cutils.h" | 33 | +#include "qemu/cutils.h" |
34 | +#include "sysemu/dma.h" | 34 | +#include "qemu/error-report.h" |
35 | +#include "system/dma.h" | ||
35 | + | 36 | + |
36 | +#include "hw/uefi/var-service.h" | 37 | +#include "hw/uefi/var-service.h" |
37 | + | 38 | + |
38 | +#include "qapi/dealloc-visitor.h" | 39 | +#include "qapi/dealloc-visitor.h" |
39 | +#include "qapi/qobject-input-visitor.h" | 40 | +#include "qapi/qobject-input-visitor.h" |
40 | +#include "qapi/qobject-output-visitor.h" | 41 | +#include "qapi/qobject-output-visitor.h" |
41 | +#include "qapi/qmp/qobject.h" | 42 | +#include "qapi/qmp/qobject.h" |
42 | +#include "qapi/qmp/qjson.h" | 43 | +#include "qapi/qmp/qjson.h" |
43 | +#include "qapi/qapi-types-uefi.h" | 44 | +#include "qapi/qapi-types-uefi.h" |
44 | +#include "qapi/qapi-visit-uefi.h" | 45 | +#include "qapi/qapi-visit-uefi.h" |
45 | + | 46 | + |
46 | +static UefiVarStore *uefi_vars_to_qapi(uefi_vars_state *uv) | 47 | +static char *generate_hexstr(void *data, size_t len) |
47 | +{ | 48 | +{ |
48 | + static const char hex[] = { | 49 | + static const char hex[] = { |
49 | + '0', '1', '2', '3', '4', '5', '6', '7', | 50 | + '0', '1', '2', '3', '4', '5', '6', '7', |
50 | + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', | 51 | + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', |
51 | + }; | 52 | + }; |
53 | + uint8_t *src = data; | ||
54 | + char *dest; | ||
55 | + size_t i; | ||
56 | + | ||
57 | + dest = g_malloc(len * 2 + 1); | ||
58 | + for (i = 0; i < len * 2;) { | ||
59 | + dest[i++] = hex[*src >> 4]; | ||
60 | + dest[i++] = hex[*src & 15]; | ||
61 | + src++; | ||
62 | + } | ||
63 | + dest[i++] = 0; | ||
64 | + | ||
65 | + return dest; | ||
66 | +} | ||
67 | + | ||
68 | +static UefiVarStore *uefi_vars_to_qapi(uefi_vars_state *uv) | ||
69 | +{ | ||
52 | + UefiVarStore *vs; | 70 | + UefiVarStore *vs; |
53 | + UefiVariableList **tail; | 71 | + UefiVariableList **tail; |
54 | + UefiVariable *v; | 72 | + UefiVariable *v; |
55 | + QemuUUID be; | 73 | + QemuUUID be; |
56 | + uefi_variable *var; | 74 | + uefi_variable *var; |
57 | + uint8_t *data; | ||
58 | + unsigned int i; | ||
59 | + | 75 | + |
60 | + vs = g_new0(UefiVarStore, 1); | 76 | + vs = g_new0(UefiVarStore, 1); |
61 | + vs->version = 2; | 77 | + vs->version = 2; |
62 | + tail = &vs->variables; | 78 | + tail = &vs->variables; |
63 | + | 79 | + |
... | ... | ||
70 | + be = qemu_uuid_bswap(var->guid); | 86 | + be = qemu_uuid_bswap(var->guid); |
71 | + v->guid = qemu_uuid_unparse_strdup(&be); | 87 | + v->guid = qemu_uuid_unparse_strdup(&be); |
72 | + v->name = uefi_ucs2_to_ascii(var->name, var->name_size); | 88 | + v->name = uefi_ucs2_to_ascii(var->name, var->name_size); |
73 | + v->attr = var->attributes; | 89 | + v->attr = var->attributes; |
74 | + | 90 | + |
75 | + v->data = g_malloc(var->data_size * 2 + 1); | 91 | + v->data = generate_hexstr(var->data, var->data_size); |
76 | + data = var->data; | 92 | + |
77 | + for (i = 0; i < var->data_size * 2;) { | 93 | + if (var->attributes & |
78 | + v->data[i++] = hex[*data >> 4]; | 94 | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { |
79 | + v->data[i++] = hex[*data & 15]; | 95 | + v->time = generate_hexstr(&var->time, sizeof(var->time)); |
80 | + data++; | 96 | + if (var->digest && var->digest_size) { |
81 | + } | 97 | + v->digest = generate_hexstr(var->digest, var->digest_size); |
82 | + v->data[i++] = 0; | 98 | + } |
99 | + } | ||
83 | + | 100 | + |
84 | + QAPI_LIST_APPEND(tail, v); | 101 | + QAPI_LIST_APPEND(tail, v); |
85 | + } | 102 | + } |
86 | + return vs; | 103 | + return vs; |
87 | +} | 104 | +} |
... | ... | ||
91 | + switch (c) { | 108 | + switch (c) { |
92 | + case '0' ... '9': return c - '0'; | 109 | + case '0' ... '9': return c - '0'; |
93 | + case 'a' ... 'f': return c - 'a' + 0xa; | 110 | + case 'a' ... 'f': return c - 'a' + 0xa; |
94 | + case 'A' ... 'F': return c - 'A' + 0xA; | 111 | + case 'A' ... 'F': return c - 'A' + 0xA; |
95 | + default: return 0; | 112 | + default: return 0; |
113 | + } | ||
114 | +} | ||
115 | + | ||
116 | +static void parse_hexstr(void *dest, char *src, int len) | ||
117 | +{ | ||
118 | + uint8_t *data = dest; | ||
119 | + size_t i; | ||
120 | + | ||
121 | + for (i = 0; i < len; i += 2) { | ||
122 | + *(data++) = | ||
123 | + parse_hexchar(src[i]) << 4 | | ||
124 | + parse_hexchar(src[i + 1]); | ||
96 | + } | 125 | + } |
97 | +} | 126 | +} |
98 | + | 127 | + |
99 | +static void uefi_vars_from_qapi(uefi_vars_state *uv, UefiVarStore *vs) | 128 | +static void uefi_vars_from_qapi(uefi_vars_state *uv, UefiVarStore *vs) |
100 | +{ | 129 | +{ |
... | ... | ||
121 | + } | 150 | + } |
122 | + | 151 | + |
123 | + len = strlen(v->data); | 152 | + len = strlen(v->data); |
124 | + var->data_size = len / 2; | 153 | + var->data_size = len / 2; |
125 | + var->data = data = g_malloc(var->data_size); | 154 | + var->data = data = g_malloc(var->data_size); |
126 | + for (i = 0; i < len; i += 2) { | 155 | + parse_hexstr(var->data, v->data, len); |
127 | + *(data++) = | 156 | + |
128 | + parse_hexchar(v->data[i]) << 4 | | 157 | + if (v->time && strlen(v->time) == 32) { |
129 | + parse_hexchar(v->data[i + 1]); | 158 | + parse_hexstr(&var->time, v->time, 32); |
159 | + } | ||
160 | + | ||
161 | + if (v->digest) { | ||
162 | + len = strlen(v->digest); | ||
163 | + var->digest_size = len / 2; | ||
164 | + var->digest = g_malloc(var->digest_size); | ||
165 | + parse_hexstr(var->digest, v->digest, len); | ||
130 | + } | 166 | + } |
131 | + | 167 | + |
132 | + QTAILQ_INSERT_TAIL(&uv->variables, var, next); | 168 | + QTAILQ_INSERT_TAIL(&uv->variables, var, next); |
133 | + } | 169 | + } |
134 | +} | 170 | +} |
... | ... | ||
161 | +} | 197 | +} |
162 | + | 198 | + |
163 | +void uefi_vars_json_save(uefi_vars_state *uv) | 199 | +void uefi_vars_json_save(uefi_vars_state *uv) |
164 | +{ | 200 | +{ |
165 | + GString *gstr; | 201 | + GString *gstr; |
202 | + int rc; | ||
166 | + | 203 | + |
167 | + if (uv->jsonfd == -1) { | 204 | + if (uv->jsonfd == -1) { |
168 | + return; | 205 | + return; |
169 | + } | 206 | + } |
170 | + | 207 | + |
171 | + gstr = uefi_vars_to_json(uv); | 208 | + gstr = uefi_vars_to_json(uv); |
172 | + | 209 | + |
173 | + lseek(uv->jsonfd, 0, SEEK_SET); | 210 | + lseek(uv->jsonfd, 0, SEEK_SET); |
174 | + write(uv->jsonfd, gstr->str, gstr->len); | 211 | + rc = write(uv->jsonfd, gstr->str, gstr->len); |
175 | + ftruncate(uv->jsonfd, gstr->len); | 212 | + if (rc != gstr->len) { |
213 | + warn_report("%s: write error", __func__); | ||
214 | + } | ||
215 | + rc = ftruncate(uv->jsonfd, gstr->len); | ||
216 | + if (rc != 0) { | ||
217 | + warn_report("%s: ftruncate error", __func__); | ||
218 | + } | ||
176 | + fsync(uv->jsonfd); | 219 | + fsync(uv->jsonfd); |
177 | + | 220 | + |
178 | + g_string_free(gstr, true); | 221 | + g_string_free(gstr, true); |
179 | +} | 222 | +} |
180 | + | 223 | + |
... | ... | ||
183 | + UefiVarStore *vs; | 226 | + UefiVarStore *vs; |
184 | + QObject *qobj; | 227 | + QObject *qobj; |
185 | + Visitor *v; | 228 | + Visitor *v; |
186 | + char *str; | 229 | + char *str; |
187 | + size_t len; | 230 | + size_t len; |
231 | + int rc; | ||
188 | + | 232 | + |
189 | + if (uv->jsonfd == -1) { | 233 | + if (uv->jsonfd == -1) { |
190 | + return; | 234 | + return; |
191 | + } | 235 | + } |
192 | + | 236 | + |
... | ... | ||
195 | + return; | 239 | + return; |
196 | + } | 240 | + } |
197 | + | 241 | + |
198 | + str = g_malloc(len + 1); | 242 | + str = g_malloc(len + 1); |
199 | + lseek(uv->jsonfd, 0, SEEK_SET); | 243 | + lseek(uv->jsonfd, 0, SEEK_SET); |
200 | + read(uv->jsonfd, str, len); | 244 | + rc = read(uv->jsonfd, str, len); |
245 | + if (rc != len) { | ||
246 | + warn_report("%s: read error", __func__); | ||
247 | + } | ||
201 | + str[len] = 0; | 248 | + str[len] = 0; |
202 | + | 249 | + |
203 | + qobj = qobject_from_json(str, errp); | 250 | + qobj = qobject_from_json(str, errp); |
204 | + v = qobject_input_visitor_new(qobj); | 251 | + v = qobject_input_visitor_new(qobj); |
205 | + visit_type_UefiVarStore(v, NULL, &vs, errp); | 252 | + visit_type_UefiVarStore(v, NULL, &vs, errp); |
206 | + visit_free(v); | 253 | + visit_free(v); |
207 | + | 254 | + |
208 | + if (!(*errp)) { | 255 | + if (!(*errp)) { |
209 | + uefi_vars_from_qapi(uv, vs); | 256 | + uefi_vars_from_qapi(uv, vs); |
257 | + uefi_vars_update_storage(uv); | ||
210 | + } | 258 | + } |
211 | + | 259 | + |
212 | + qapi_free_UefiVarStore(vs); | 260 | + qapi_free_UefiVarStore(vs); |
213 | + qobject_unref(qobj); | 261 | + qobject_unref(qobj); |
214 | + g_free(str); | 262 | + g_free(str); |
215 | +} | 263 | +} |
216 | diff --git a/qapi/meson.build b/qapi/meson.build | 264 | diff --git a/qapi/meson.build b/qapi/meson.build |
217 | index XXXXXXX..XXXXXXX 100644 | 265 | index XXXXXXX..XXXXXXX 100644 |
218 | --- a/qapi/meson.build | 266 | --- a/qapi/meson.build |
219 | +++ b/qapi/meson.build | 267 | +++ b/qapi/meson.build |
220 | @@ -XXX,XX +XXX,XX @@ if have_system | 268 | @@ -XXX,XX +XXX,XX @@ if have_system |
221 | 'rdma', | 269 | 'pci', |
222 | 'rocker', | 270 | 'rocker', |
223 | 'tpm', | 271 | 'tpm', |
224 | + 'uefi', | 272 | + 'uefi', |
225 | ] | 273 | ] |
226 | endif | 274 | endif |
227 | if have_system or have_tools | 275 | if have_system or have_tools |
228 | diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json | 276 | diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json |
229 | index XXXXXXX..XXXXXXX 100644 | 277 | index XXXXXXX..XXXXXXX 100644 |
230 | --- a/qapi/qapi-schema.json | 278 | --- a/qapi/qapi-schema.json |
231 | +++ b/qapi/qapi-schema.json | 279 | +++ b/qapi/qapi-schema.json |
232 | @@ -XXX,XX +XXX,XX @@ | 280 | @@ -XXX,XX +XXX,XX @@ |
233 | { 'include': 'virtio.json' } | 281 | { 'include': 'vfio.json' } |
234 | { 'include': 'cryptodev.json' } | 282 | { 'include': 'cryptodev.json' } |
235 | { 'include': 'cxl.json' } | 283 | { 'include': 'cxl.json' } |
236 | +{ 'include': 'uefi.json' } | 284 | +{ 'include': 'uefi.json' } |
237 | diff --git a/qapi/uefi.json b/qapi/uefi.json | 285 | diff --git a/qapi/uefi.json b/qapi/uefi.json |
238 | new file mode 100644 | 286 | new file mode 100644 |
... | ... | ||
255 | +# | 303 | +# |
256 | +# @attr: variable attributes | 304 | +# @attr: variable attributes |
257 | +# | 305 | +# |
258 | +# @data: variable content (base64) | 306 | +# @data: variable content (base64) |
259 | +# | 307 | +# |
260 | +# Since: 9.0 | 308 | +# @time: variable modification time (EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS). |
309 | +# | ||
310 | +# @digest: variable certificate digest (EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS). | ||
311 | +# | ||
312 | +# Since: 10.0 | ||
261 | +## | 313 | +## |
262 | +{ 'struct' : 'UefiVariable', | 314 | +{ 'struct' : 'UefiVariable', |
263 | + 'data' : { 'guid' : 'str', | 315 | + 'data' : { 'guid' : 'str', |
264 | + 'name' : 'str', | 316 | + 'name' : 'str', |
265 | + 'attr' : 'int', | 317 | + 'attr' : 'int', |
266 | + 'data' : 'str', | 318 | + 'data' : 'str', |
267 | + '*time' : 'str'}} | 319 | + '*time' : 'str', |
320 | + '*digest' : 'str'}} | ||
268 | + | 321 | + |
269 | +## | 322 | +## |
270 | +# @UefiVarStore: | 323 | +# @UefiVarStore: |
271 | +# | 324 | +# |
272 | +# UEFI Variable Store | 325 | +# UEFI Variable Store |
273 | +# | 326 | +# |
274 | +# @version: 2 | 327 | +# @version: 2 |
275 | +# | 328 | +# |
276 | +# @variables: list of uefi variables | 329 | +# @variables: list of uefi variables |
277 | +# | 330 | +# |
278 | +# Since: 9.0 | 331 | +# Since: 10.0 |
279 | +## | 332 | +## |
280 | +{ 'struct' : 'UefiVarStore', | 333 | +{ 'struct' : 'UefiVarStore', |
281 | + 'data' : { 'version' : 'int', | 334 | + 'data' : { 'version' : 'int', |
282 | + 'variables' : [ 'UefiVariable' ] }} | 335 | + 'variables' : [ 'UefiVariable' ] }} |
283 | -- | 336 | -- |
284 | 2.41.0 | 337 | 2.47.1 | diff view generated by jsdifflib |
1 | Add trace events for debugging and trouble shooting. | ||
---|---|---|---|
2 | |||
1 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 3 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
2 | --- | 4 | --- |
3 | hw/uefi/trace-events | 16 ++++++++++++++++ | 5 | hw/uefi/trace-events | 17 +++++++++++++++++ |
4 | 1 file changed, 16 insertions(+) | 6 | 1 file changed, 17 insertions(+) |
5 | create mode 100644 hw/uefi/trace-events | 7 | create mode 100644 hw/uefi/trace-events |
6 | 8 | ||
7 | diff --git a/hw/uefi/trace-events b/hw/uefi/trace-events | 9 | diff --git a/hw/uefi/trace-events b/hw/uefi/trace-events |
8 | new file mode 100644 | 10 | new file mode 100644 |
9 | index XXXXXXX..XXXXXXX | 11 | index XXXXXXX..XXXXXXX |
10 | --- /dev/null | 12 | --- /dev/null |
11 | +++ b/hw/uefi/trace-events | 13 | +++ b/hw/uefi/trace-events |
12 | @@ -XXX,XX +XXX,XX @@ | 14 | @@ -XXX,XX +XXX,XX @@ |
13 | +# device | 15 | +# device |
14 | +uefi_reg_read(uint64_t addr, unsigned size) "addr 0x%lx, size %d" | 16 | +uefi_reg_read(uint64_t addr, unsigned size) "addr 0x%" PRIx64 ", size %u" |
15 | +uefi_reg_write(uint64_t addr, uint64_t val, unsigned size) "addr 0x%lx, val 0x%lx, size %d" | 17 | +uefi_reg_write(uint64_t addr, uint64_t val, unsigned size) "addr 0x%" PRIx64 ", val 0x%" PRIx64 ", size %d" |
16 | +uefi_hard_reset(void) "" | 18 | +uefi_hard_reset(void) "" |
17 | + | 19 | + |
18 | +# generic uefi | 20 | +# generic uefi |
19 | +uefi_variable(const char *context, const char *name, uint64_t size, const char *uuid) "context %s, name %s, size %ld, uuid %s" | 21 | +uefi_variable(const char *context, const char *name, uint64_t size, const char *uuid) "context %s, name %s, size %" PRIu64 ", uuid %s" |
20 | +uefi_status(const char *context, const char *name) "context %s, status %s" | 22 | +uefi_status(const char *context, const char *name) "context %s, status %s" |
21 | +uefi_event(const char *name) "event %s" | 23 | +uefi_event(const char *name) "event %s" |
22 | + | 24 | + |
23 | +# variable protocol | 25 | +# variable protocol |
24 | +uefi_vars_proto_cmd(const char *cmd) "cmd %s" | 26 | +uefi_vars_proto_cmd(const char *cmd) "cmd %s" |
27 | +uefi_vars_security_violation(const char *reason) "reason %s" | ||
25 | + | 28 | + |
26 | +# variable policy protocol | 29 | +# variable policy protocol |
27 | +uefi_vars_policy_cmd(const char *cmd) "cmd %s" | 30 | +uefi_vars_policy_cmd(const char *cmd) "cmd %s" |
28 | +uefi_vars_policy_deny(const char *reason) "reason %s" | 31 | +uefi_vars_policy_deny(const char *reason) "reason %s" |
29 | -- | 32 | -- |
30 | 2.41.0 | 33 | 2.47.1 | diff view generated by jsdifflib |
1 | Add UEFI_VARS config option, enable by default for x86_64 and aarch64. | ||
---|---|---|---|
2 | |||
1 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 3 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
2 | --- | 4 | --- |
3 | hw/Kconfig | 1 + | 5 | hw/Kconfig | 1 + |
4 | hw/uefi/Kconfig | 3 +++ | 6 | hw/uefi/Kconfig | 3 +++ |
5 | 2 files changed, 4 insertions(+) | 7 | 2 files changed, 4 insertions(+) |
... | ... | ||
25 | @@ -XXX,XX +XXX,XX @@ | 27 | @@ -XXX,XX +XXX,XX @@ |
26 | +config UEFI_VARS | 28 | +config UEFI_VARS |
27 | + bool | 29 | + bool |
28 | + default y if X86_64 || AARCH64 | 30 | + default y if X86_64 || AARCH64 |
29 | -- | 31 | -- |
30 | 2.41.0 | 32 | 2.47.1 | diff view generated by jsdifflib |
1 | Wire up uefi-vars in the build system. | 1 | Wire up uefi-vars in the build system. |
---|---|---|---|
2 | 2 | ||
3 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 3 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
4 | --- | 4 | --- |
5 | hw/meson.build | 1 + | 5 | hw/meson.build | 1 + |
6 | hw/uefi/meson.build | 12 ++++++++++++ | 6 | hw/uefi/meson.build | 18 ++++++++++++++++++ |
7 | meson.build | 1 + | 7 | meson.build | 1 + |
8 | 3 files changed, 14 insertions(+) | 8 | 3 files changed, 20 insertions(+) |
9 | create mode 100644 hw/uefi/meson.build | 9 | create mode 100644 hw/uefi/meson.build |
10 | 10 | ||
11 | diff --git a/hw/meson.build b/hw/meson.build | 11 | diff --git a/hw/meson.build b/hw/meson.build |
12 | index XXXXXXX..XXXXXXX 100644 | 12 | index XXXXXXX..XXXXXXX 100644 |
13 | --- a/hw/meson.build | 13 | --- a/hw/meson.build |
... | ... | ||
25 | index XXXXXXX..XXXXXXX | 25 | index XXXXXXX..XXXXXXX |
26 | --- /dev/null | 26 | --- /dev/null |
27 | +++ b/hw/uefi/meson.build | 27 | +++ b/hw/uefi/meson.build |
28 | @@ -XXX,XX +XXX,XX @@ | 28 | @@ -XXX,XX +XXX,XX @@ |
29 | +uefi_vars_ss = ss.source_set() | 29 | +uefi_vars_ss = ss.source_set() |
30 | +uefi_vars_ss.add(when: 'CONFIG_UEFI_VARS', | 30 | +if (config_all_devices.has_key('CONFIG_UEFI_VARS')) |
31 | + if_true: files('var-service-core.c', | 31 | + uefi_vars_ss.add(files('var-service-core.c', |
32 | + 'var-service-json.c', | 32 | + 'var-service-json.c', |
33 | + 'var-service-vars.c', | 33 | + 'var-service-vars.c', |
34 | + 'var-service-auth.c', | 34 | + 'var-service-auth.c', |
35 | + 'var-service-guid.c', | 35 | + 'var-service-guid.c', |
36 | + 'var-service-policy.c')) | 36 | + 'var-service-utils.c', |
37 | + 'var-service-policy.c')) | ||
38 | + uefi_vars_ss.add(when: gnutls, | ||
39 | + if_true: files('var-service-pkcs7.c'), | ||
40 | + if_false: files('var-service-pkcs7-stub.c')) | ||
41 | + uefi_vars_ss.add(files('var-service-siglist.c')) | ||
42 | +endif | ||
37 | + | 43 | + |
38 | +modules += { 'hw-uefi' : { | 44 | +modules += { 'hw-uefi' : { |
39 | + 'vars' : uefi_vars_ss, | 45 | + 'vars' : uefi_vars_ss, |
40 | +}} | 46 | +}} |
41 | diff --git a/meson.build b/meson.build | 47 | diff --git a/meson.build b/meson.build |
... | ... | ||
49 | + 'hw/uefi', | 55 | + 'hw/uefi', |
50 | 'hw/ufs', | 56 | 'hw/ufs', |
51 | 'hw/usb', | 57 | 'hw/usb', |
52 | 'hw/vfio', | 58 | 'hw/vfio', |
53 | -- | 59 | -- |
54 | 2.41.0 | 60 | 2.47.1 | diff view generated by jsdifflib |
1 | This adds sysbus bindings for the variable service. | 1 | This adds sysbus bindings for the variable service. |
---|---|---|---|
2 | 2 | ||
3 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 3 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
4 | --- | 4 | --- |
5 | hw/uefi/var-service-sysbus.c | 87 ++++++++++++++++++++++++++++++++++++ | 5 | hw/uefi/var-service-sysbus.c | 90 ++++++++++++++++++++++++++++++++++++ |
6 | hw/uefi/meson.build | 3 +- | 6 | hw/uefi/meson.build | 3 +- |
7 | 2 files changed, 89 insertions(+), 1 deletion(-) | 7 | 2 files changed, 92 insertions(+), 1 deletion(-) |
8 | create mode 100644 hw/uefi/var-service-sysbus.c | 8 | create mode 100644 hw/uefi/var-service-sysbus.c |
9 | 9 | ||
10 | diff --git a/hw/uefi/var-service-sysbus.c b/hw/uefi/var-service-sysbus.c | 10 | diff --git a/hw/uefi/var-service-sysbus.c b/hw/uefi/var-service-sysbus.c |
11 | new file mode 100644 | 11 | new file mode 100644 |
12 | index XXXXXXX..XXXXXXX | 12 | index XXXXXXX..XXXXXXX |
... | ... | ||
42 | + vmstate_uefi_vars, uefi_vars_state), | 42 | + vmstate_uefi_vars, uefi_vars_state), |
43 | + VMSTATE_END_OF_LIST() | 43 | + VMSTATE_END_OF_LIST() |
44 | + } | 44 | + } |
45 | +}; | 45 | +}; |
46 | + | 46 | + |
47 | +static Property uefi_vars_sysbus_properties[] = { | 47 | +static const Property uefi_vars_sysbus_properties[] = { |
48 | + DEFINE_PROP_SIZE("size", uefi_vars_sysbus_state, state.max_storage, | 48 | + DEFINE_PROP_SIZE("size", uefi_vars_sysbus_state, state.max_storage, |
49 | + 256 * 1024), | 49 | + 256 * 1024), |
50 | + DEFINE_PROP_STRING("jsonfile", uefi_vars_sysbus_state, state.jsonfile), | 50 | + DEFINE_PROP_STRING("jsonfile", uefi_vars_sysbus_state, state.jsonfile), |
51 | + DEFINE_PROP_END_OF_LIST(), | 51 | + DEFINE_PROP_BOOL("force-secure-boot", uefi_vars_sysbus_state, |
52 | + state.force_secure_boot, false), | ||
53 | + DEFINE_PROP_BOOL("disable-custom-mode", uefi_vars_sysbus_state, | ||
54 | + state.disable_custom_mode, false), | ||
52 | +}; | 55 | +}; |
53 | + | 56 | + |
54 | +static void uefi_vars_sysbus_init(Object *obj) | 57 | +static void uefi_vars_sysbus_init(Object *obj) |
55 | +{ | 58 | +{ |
56 | + uefi_vars_sysbus_state *uv = UEFI_VARS_SYSBUS(obj); | 59 | + uefi_vars_sysbus_state *uv = UEFI_VARS_SYSBUS(obj); |
... | ... | ||
77 | +static void uefi_vars_sysbus_class_init(ObjectClass *klass, void *data) | 80 | +static void uefi_vars_sysbus_class_init(ObjectClass *klass, void *data) |
78 | +{ | 81 | +{ |
79 | + DeviceClass *dc = DEVICE_CLASS(klass); | 82 | + DeviceClass *dc = DEVICE_CLASS(klass); |
80 | + | 83 | + |
81 | + dc->realize = uefi_vars_sysbus_realize; | 84 | + dc->realize = uefi_vars_sysbus_realize; |
82 | + dc->reset = uefi_vars_sysbus_reset; | ||
83 | + dc->vmsd = &vmstate_uefi_vars_sysbus; | 85 | + dc->vmsd = &vmstate_uefi_vars_sysbus; |
86 | + device_class_set_legacy_reset(dc, uefi_vars_sysbus_reset); | ||
84 | + device_class_set_props(dc, uefi_vars_sysbus_properties); | 87 | + device_class_set_props(dc, uefi_vars_sysbus_properties); |
85 | + set_bit(DEVICE_CATEGORY_MISC, dc->categories); | 88 | + set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
86 | +} | 89 | +} |
87 | + | 90 | + |
88 | +static const TypeInfo uefi_vars_sysbus_info = { | 91 | +static const TypeInfo uefi_vars_sysbus_info = { |
... | ... | ||
102 | +type_init(uefi_vars_sysbus_register_types) | 105 | +type_init(uefi_vars_sysbus_register_types) |
103 | diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build | 106 | diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build |
104 | index XXXXXXX..XXXXXXX 100644 | 107 | index XXXXXXX..XXXXXXX 100644 |
105 | --- a/hw/uefi/meson.build | 108 | --- a/hw/uefi/meson.build |
106 | +++ b/hw/uefi/meson.build | 109 | +++ b/hw/uefi/meson.build |
107 | @@ -XXX,XX +XXX,XX @@ uefi_vars_ss.add(when: 'CONFIG_UEFI_VARS', | 110 | @@ -XXX,XX +XXX,XX @@ if (config_all_devices.has_key('CONFIG_UEFI_VARS')) |
108 | 'var-service-vars.c', | 111 | 'var-service-auth.c', |
109 | 'var-service-auth.c', | 112 | 'var-service-guid.c', |
110 | 'var-service-guid.c', | 113 | 'var-service-utils.c', |
111 | - 'var-service-policy.c')) | 114 | - 'var-service-policy.c')) |
112 | + 'var-service-policy.c', | 115 | + 'var-service-policy.c', |
113 | + 'var-service-sysbus.c')) | 116 | + 'var-service-sysbus.c')) |
114 | 117 | uefi_vars_ss.add(when: gnutls, | |
115 | modules += { 'hw-uefi' : { | 118 | if_true: files('var-service-pkcs7.c'), |
116 | 'vars' : uefi_vars_ss, | 119 | if_false: files('var-service-pkcs7-stub.c')) |
117 | -- | 120 | -- |
118 | 2.41.0 | 121 | 2.47.1 | diff view generated by jsdifflib |
1 | This adds isa bindings for the variable service. | 1 | This adds isa bindings for the variable service. |
---|---|---|---|
2 | 2 | ||
3 | Usage: qemu-system-x86_64 -device uefi-vars-isa,jsonfile=/path/to/uefivars.json | 3 | Usage: qemu-system-x86_64 -device uefi-vars-isa,jsonfile=/path/to/uefivars.json |
4 | 4 | ||
5 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 5 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
6 | --- | 6 | --- |
7 | hw/uefi/var-service-isa.c | 88 +++++++++++++++++++++++++++++++++++++++ | 7 | hw/uefi/var-service-isa.c | 91 +++++++++++++++++++++++++++++++++++++++ |
8 | hw/uefi/Kconfig | 6 +++ | 8 | hw/uefi/Kconfig | 6 +++ |
9 | hw/uefi/meson.build | 5 +++ | 9 | hw/uefi/meson.build | 5 +++ |
10 | 3 files changed, 99 insertions(+) | 10 | 3 files changed, 102 insertions(+) |
11 | create mode 100644 hw/uefi/var-service-isa.c | 11 | create mode 100644 hw/uefi/var-service-isa.c |
12 | 12 | ||
13 | diff --git a/hw/uefi/var-service-isa.c b/hw/uefi/var-service-isa.c | 13 | diff --git a/hw/uefi/var-service-isa.c b/hw/uefi/var-service-isa.c |
14 | new file mode 100644 | 14 | new file mode 100644 |
15 | index XXXXXXX..XXXXXXX | 15 | index XXXXXXX..XXXXXXX |
... | ... | ||
45 | + vmstate_uefi_vars, uefi_vars_state), | 45 | + vmstate_uefi_vars, uefi_vars_state), |
46 | + VMSTATE_END_OF_LIST() | 46 | + VMSTATE_END_OF_LIST() |
47 | + } | 47 | + } |
48 | +}; | 48 | +}; |
49 | + | 49 | + |
50 | +static Property uefi_vars_isa_properties[] = { | 50 | +static const Property uefi_vars_isa_properties[] = { |
51 | + DEFINE_PROP_SIZE("size", uefi_vars_isa_state, state.max_storage, | 51 | + DEFINE_PROP_SIZE("size", uefi_vars_isa_state, state.max_storage, |
52 | + 256 * 1024), | 52 | + 256 * 1024), |
53 | + DEFINE_PROP_STRING("jsonfile", uefi_vars_isa_state, state.jsonfile), | 53 | + DEFINE_PROP_STRING("jsonfile", uefi_vars_isa_state, state.jsonfile), |
54 | + DEFINE_PROP_END_OF_LIST(), | 54 | + DEFINE_PROP_BOOL("force-secure-boot", uefi_vars_isa_state, |
55 | + state.force_secure_boot, false), | ||
56 | + DEFINE_PROP_BOOL("disable-custom-mode", uefi_vars_isa_state, | ||
57 | + state.disable_custom_mode, false), | ||
55 | +}; | 58 | +}; |
56 | + | 59 | + |
57 | +static void uefi_vars_isa_init(Object *obj) | 60 | +static void uefi_vars_isa_init(Object *obj) |
58 | +{ | 61 | +{ |
59 | + uefi_vars_isa_state *uv = UEFI_VARS_ISA(obj); | 62 | + uefi_vars_isa_state *uv = UEFI_VARS_ISA(obj); |
... | ... | ||
80 | +static void uefi_vars_isa_class_init(ObjectClass *klass, void *data) | 83 | +static void uefi_vars_isa_class_init(ObjectClass *klass, void *data) |
81 | +{ | 84 | +{ |
82 | + DeviceClass *dc = DEVICE_CLASS(klass); | 85 | + DeviceClass *dc = DEVICE_CLASS(klass); |
83 | + | 86 | + |
84 | + dc->realize = uefi_vars_isa_realize; | 87 | + dc->realize = uefi_vars_isa_realize; |
85 | + dc->reset = uefi_vars_isa_reset; | ||
86 | + dc->vmsd = &vmstate_uefi_vars_isa; | 88 | + dc->vmsd = &vmstate_uefi_vars_isa; |
89 | + device_class_set_legacy_reset(dc, uefi_vars_isa_reset); | ||
87 | + device_class_set_props(dc, uefi_vars_isa_properties); | 90 | + device_class_set_props(dc, uefi_vars_isa_properties); |
88 | + set_bit(DEVICE_CATEGORY_MISC, dc->categories); | 91 | + set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
89 | +} | 92 | +} |
90 | + | 93 | + |
91 | +static const TypeInfo uefi_vars_isa_info = { | 94 | +static const TypeInfo uefi_vars_isa_info = { |
... | ... | ||
120 | + depends on ISA_BUS | 123 | + depends on ISA_BUS |
121 | diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build | 124 | diff --git a/hw/uefi/meson.build b/hw/uefi/meson.build |
122 | index XXXXXXX..XXXXXXX 100644 | 125 | index XXXXXXX..XXXXXXX 100644 |
123 | --- a/hw/uefi/meson.build | 126 | --- a/hw/uefi/meson.build |
124 | +++ b/hw/uefi/meson.build | 127 | +++ b/hw/uefi/meson.build |
125 | @@ -XXX,XX +XXX,XX @@ uefi_vars_ss.add(when: 'CONFIG_UEFI_VARS', | 128 | @@ -XXX,XX +XXX,XX @@ if (config_all_devices.has_key('CONFIG_UEFI_VARS')) |
126 | 'var-service-policy.c', | 129 | uefi_vars_ss.add(files('var-service-siglist.c')) |
127 | 'var-service-sysbus.c')) | 130 | endif |
128 | 131 | ||
129 | +uefi_vars_isa_ss = ss.source_set() | 132 | +uefi_vars_isa_ss = ss.source_set() |
130 | +uefi_vars_isa_ss.add(when: 'CONFIG_UEFI_VARS_ISA', | 133 | +uefi_vars_isa_ss.add(when: 'CONFIG_UEFI_VARS_ISA', |
131 | + if_true: files('var-service-isa.c')) | 134 | + if_true: files('var-service-isa.c')) |
132 | + | 135 | + |
133 | modules += { 'hw-uefi' : { | 136 | modules += { 'hw-uefi' : { |
134 | 'vars' : uefi_vars_ss, | 137 | 'vars' : uefi_vars_ss, |
135 | + 'vars-isa' : uefi_vars_isa_ss, | 138 | + 'vars-isa' : uefi_vars_isa_ss, |
136 | }} | 139 | }} |
137 | -- | 140 | -- |
138 | 2.41.0 | 141 | 2.47.1 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
20 | + VIRT_UEFI_VARS, | 20 | + VIRT_UEFI_VARS, |
21 | VIRT_LOWMEMMAP_LAST, | 21 | VIRT_LOWMEMMAP_LAST, |
22 | }; | 22 | }; |
23 | 23 | ||
24 | @@ -XXX,XX +XXX,XX @@ struct VirtMachineState { | 24 | @@ -XXX,XX +XXX,XX @@ struct VirtMachineState { |
25 | bool ras; | ||
26 | bool mte; | 25 | bool mte; |
27 | bool dtb_randomness; | 26 | bool dtb_randomness; |
27 | bool second_ns_uart_present; | ||
28 | + bool uefi_vars; | 28 | + bool uefi_vars; |
29 | OnOffAuto acpi; | 29 | OnOffAuto acpi; |
30 | VirtGICType gic_version; | 30 | VirtGICType gic_version; |
31 | VirtIOMMUType iommu; | 31 | VirtIOMMUType iommu; |
32 | diff --git a/hw/arm/virt.c b/hw/arm/virt.c | 32 | diff --git a/hw/arm/virt.c b/hw/arm/virt.c |
... | ... | ||
37 | #include "hw/intc/arm_gicv3_common.h" | 37 | #include "hw/intc/arm_gicv3_common.h" |
38 | #include "hw/intc/arm_gicv3_its_common.h" | 38 | #include "hw/intc/arm_gicv3_its_common.h" |
39 | #include "hw/irq.h" | 39 | #include "hw/irq.h" |
40 | +#include "hw/uefi/var-service-api.h" | 40 | +#include "hw/uefi/var-service-api.h" |
41 | #include "kvm_arm.h" | 41 | #include "kvm_arm.h" |
42 | #include "hvf_arm.h" | ||
42 | #include "hw/firmware/smbios.h" | 43 | #include "hw/firmware/smbios.h" |
43 | #include "qapi/visitor.h" | ||
44 | @@ -XXX,XX +XXX,XX @@ static const MemMapEntry base_memmap[] = { | 44 | @@ -XXX,XX +XXX,XX @@ static const MemMapEntry base_memmap[] = { |
45 | [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, | 45 | [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, |
46 | [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, | 46 | [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, |
47 | [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, | 47 | [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, |
48 | + [VIRT_UEFI_VARS] = { 0x090c0000, 0x00000010 }, | 48 | + [VIRT_UEFI_VARS] = { 0x090c0000, 0x00000010 }, |
... | ... | ||
115 | + virt_set_uefi_vars); | 115 | + virt_set_uefi_vars); |
116 | } | 116 | } |
117 | 117 | ||
118 | static void virt_instance_init(Object *obj) | 118 | static void virt_instance_init(Object *obj) |
119 | -- | 119 | -- |
120 | 2.41.0 | 120 | 2.47.1 | diff view generated by jsdifflib |
1 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | 1 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> |
---|---|---|---|
2 | --- | 2 | --- |
3 | docs/devel/index-internals.rst | 1 + | 3 | docs/devel/index-internals.rst | 1 + |
4 | docs/devel/uefi-vars.rst | 66 ++++++++++++++++++++++++++++++++++ | 4 | docs/devel/uefi-vars.rst | 66 ++++++++++++++++++++++++++++++++++ |
5 | hw/uefi/TODO.md | 17 +++++++++ | 5 | hw/uefi/LIMITATIONS.md | 7 ++++ |
6 | 3 files changed, 84 insertions(+) | 6 | 3 files changed, 74 insertions(+) |
7 | create mode 100644 docs/devel/uefi-vars.rst | 7 | create mode 100644 docs/devel/uefi-vars.rst |
8 | create mode 100644 hw/uefi/TODO.md | 8 | create mode 100644 hw/uefi/LIMITATIONS.md |
9 | 9 | ||
10 | diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst | 10 | diff --git a/docs/devel/index-internals.rst b/docs/devel/index-internals.rst |
11 | index XXXXXXX..XXXXXXX 100644 | 11 | index XXXXXXX..XXXXXXX 100644 |
12 | --- a/docs/devel/index-internals.rst | 12 | --- a/docs/devel/index-internals.rst |
13 | +++ b/docs/devel/index-internals.rst | 13 | +++ b/docs/devel/index-internals.rst |
14 | @@ -XXX,XX +XXX,XX @@ Details about QEMU's various subsystems including how to add features to them. | 14 | @@ -XXX,XX +XXX,XX @@ Details about QEMU's various subsystems including how to add features to them. |
15 | s390-cpu-topology | 15 | s390-cpu-topology |
16 | s390-dasd-ipl | 16 | s390-dasd-ipl |
17 | tracing | 17 | tracing |
18 | + uefi-vars | 18 | + uefi-vars |
19 | vfio-migration | 19 | vfio-iommufd |
20 | writing-monitor-commands | 20 | writing-monitor-commands |
21 | virtio-backends | 21 | virtio-backends |
22 | diff --git a/docs/devel/uefi-vars.rst b/docs/devel/uefi-vars.rst | 22 | diff --git a/docs/devel/uefi-vars.rst b/docs/devel/uefi-vars.rst |
23 | new file mode 100644 | 23 | new file mode 100644 |
24 | index XXXXXXX..XXXXXXX | 24 | index XXXXXXX..XXXXXXX |
... | ... | ||
30 | +============== | 30 | +============== |
31 | + | 31 | + |
32 | +Guest UEFI variable management | 32 | +Guest UEFI variable management |
33 | +============================== | 33 | +============================== |
34 | + | 34 | + |
35 | +Traditional approach for UEFI Variable storage in qemu guests is to | 35 | +The traditional approach for UEFI Variable storage in qemu guests is |
36 | +work as close as possible to physical hardware. That means provide | 36 | +to work as close as possible to physical hardware. That means |
37 | +pflash as storage and leave the management of variables and flash to | 37 | +providing pflash as storage and leaving the management of variables |
38 | +the guest. | 38 | +and flash to the guest. |
39 | + | 39 | + |
40 | +Secure boot support comes with the requirement that the UEFI variable | 40 | +Secure boot support comes with the requirement that the UEFI variable |
41 | +storage must be protected against direct access by the OS. All update | 41 | +storage must be protected against direct access by the OS. All update |
42 | +requests must pass the sanity checks. (Parts of) the firmware must | 42 | +requests must pass the sanity checks. (Parts of) the firmware must |
43 | +run with a higher priviledge level than the OS so this can be enforced | 43 | +run with a higher privilege level than the OS so this can be enforced |
44 | +by the firmware. On x86 this has been implemented using System | 44 | +by the firmware. On x86 this has been implemented using System |
45 | +Management Mode (SMM) in qemu and kvm, which again is the same | 45 | +Management Mode (SMM) in qemu and kvm, which again is the same |
46 | +approach taken by physical hardware. Only priviedged code running in | 46 | +approach taken by physical hardware. Only privileged code running in |
47 | +SMM mode is allowed to access flash storage. | 47 | +SMM mode is allowed to access flash storage. |
48 | + | 48 | + |
49 | +Communication with the firmware code running in SMM mode works by | 49 | +Communication with the firmware code running in SMM mode works by |
50 | +serializing the requests to a shared buffer, then trapping into SMM | 50 | +serializing the requests to a shared buffer, then trapping into SMM |
51 | +mode via SMI. The SMM code processes the request, stores the reply in | 51 | +mode via SMI. The SMM code processes the request, stores the reply in |
52 | +the same buffer and returns. | 52 | +the same buffer and returns. |
53 | + | 53 | + |
54 | +Host UEFI variable service | 54 | +Host UEFI variable service |
55 | +========================== | 55 | +========================== |
56 | + | 56 | + |
57 | +Instead of running the priviledged code inside the guest we can run it | 57 | +Instead of running the privileged code inside the guest we can run it |
58 | +on the host. The serialization protocol cen be reused. The | 58 | +on the host. The serialization protocol can be reused. The |
59 | +communication with the host uses a virtual device, which essentially | 59 | +communication with the host uses a virtual device, which essentially |
60 | +allows to configure the shared buffer location and size and to trap to | 60 | +configures the shared buffer location and size, and traps to the host |
61 | +the host to process the requests. | 61 | +to process the requests. |
62 | + | 62 | + |
63 | +The ``uefi-vars`` device implements the UEFI virtual device. It comes | 63 | +The ``uefi-vars`` device implements the UEFI virtual device. It comes |
64 | +in ``uefi-vars-isa`` and ``uefi-vars-sysbus`` flavours. The device | 64 | +in ``uefi-vars-isa`` and ``uefi-vars-sysbus`` flavours. The device |
65 | +reimplements the handlers needed, specifically | 65 | +reimplements the handlers needed, specifically |
66 | +``EfiSmmVariableProtocol`` and ``VarCheckPolicyLibMmiHandler``. It | 66 | +``EfiSmmVariableProtocol`` and ``VarCheckPolicyLibMmiHandler``. It |
67 | +also consumes events (``EfiEndOfDxeEventGroup``, | 67 | +also consumes events (``EfiEndOfDxeEventGroup``, |
68 | +``EfiEventReadyToBoot`` and ``EfiEventExitBootServices``). | 68 | +``EfiEventReadyToBoot`` and ``EfiEventExitBootServices``). |
69 | + | 69 | + |
70 | +The advantage of the approach is that we do not need a special | 70 | +The advantage of the approach is that we do not need a special |
71 | +prividge level for the firmware to protect itself, i.e. it does not | 71 | +privilege level for the firmware to protect itself, i.e. it does not |
72 | +depend on SMM emulation on x64, which allows to remove a bunch of | 72 | +depend on SMM emulation on x64, which allows the removal of a bunch of |
73 | +complex code for SMM emulation from the linux kernel | 73 | +complex code for SMM emulation from the linux kernel |
74 | +(CONFIG_KVM_SMM=n). It also allows to support secure boot on arm | 74 | +(CONFIG_KVM_SMM=n). It also allows support for secure boot on arm |
75 | +without implementing secure world (el3) emulation in kvm. | 75 | +without implementing secure world (el3) emulation in kvm. |
76 | + | 76 | + |
77 | +Of course there are also downsides. The added device increases the | 77 | +Of course there are also downsides. The added device increases the |
78 | +attack surface of the host, and we are adding some code duplication | 78 | +attack surface of the host, and we are adding some code duplication |
79 | +because we have to reimplement some edk2 functionality in qemu. | 79 | +because we have to reimplement some edk2 functionality in qemu. |
... | ... | ||
89 | +------------------------- | 89 | +------------------------- |
90 | + | 90 | + |
91 | +.. code:: | 91 | +.. code:: |
92 | + | 92 | + |
93 | + qemu-system-aarch64 -M virt,x-uefi-vars=on | 93 | + qemu-system-aarch64 -M virt,x-uefi-vars=on |
94 | diff --git a/hw/uefi/TODO.md b/hw/uefi/TODO.md | 94 | diff --git a/hw/uefi/LIMITATIONS.md b/hw/uefi/LIMITATIONS.md |
95 | new file mode 100644 | 95 | new file mode 100644 |
96 | index XXXXXXX..XXXXXXX | 96 | index XXXXXXX..XXXXXXX |
97 | --- /dev/null | 97 | --- /dev/null |
98 | +++ b/hw/uefi/TODO.md | 98 | +++ b/hw/uefi/LIMITATIONS.md |
99 | @@ -XXX,XX +XXX,XX @@ | 99 | @@ -XXX,XX +XXX,XX @@ |
100 | + | ||
101 | +uefi variable service - todo list | ||
102 | +--------------------------------- | ||
103 | + | ||
104 | +* implement reading/writing variable update time. | ||
105 | +* implement authenticated variable updates. | ||
106 | + - used for 'dbx' updates. | ||
107 | + | ||
108 | +known issues and limitations | 100 | +known issues and limitations |
109 | +---------------------------- | 101 | +---------------------------- |
110 | + | 102 | + |
111 | +* secure boot variables are read-only | ||
112 | + - due to auth vars not being implemented yet. | ||
113 | +* works only on little endian hosts | 103 | +* works only on little endian hosts |
114 | + - accessing structs in guest ram is done without endian conversion. | 104 | + - accessing structs in guest ram is done without endian conversion. |
115 | +* works only for 64-bit guests | 105 | +* works only for 64-bit guests |
116 | + - UINTN is mapped to uint64_t, for 32-bit guests that would be uint32_t | 106 | + - UINTN is mapped to uint64_t, for 32-bit guests that would be uint32_t |
117 | -- | 107 | -- |
118 | 2.41.0 | 108 | 2.47.1 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> | ||
2 | --- | ||
3 | MAINTAINERS | 6 ++++++ | ||
4 | 1 file changed, 6 insertions(+) | ||
1 | 5 | ||
6 | diff --git a/MAINTAINERS b/MAINTAINERS | ||
7 | index XXXXXXX..XXXXXXX 100644 | ||
8 | --- a/MAINTAINERS | ||
9 | +++ b/MAINTAINERS | ||
10 | @@ -XXX,XX +XXX,XX @@ F: hw/hyperv/hv-balloon*.h | ||
11 | F: include/hw/hyperv/dynmem-proto.h | ||
12 | F: include/hw/hyperv/hv-balloon.h | ||
13 | |||
14 | +UEFI variable service | ||
15 | +M: Gerd Hoffmann <kraxel@redhat.com> | ||
16 | +S: Maintained | ||
17 | +F: hw/uefi/ | ||
18 | +F: include/hw/uefi/ | ||
19 | + | ||
20 | Subsystems | ||
21 | ---------- | ||
22 | Overall Audio backends | ||
23 | -- | ||
24 | 2.47.1 | diff view generated by jsdifflib |