CCF
Loading...
Searching...
No Matches
snp_ioctl6.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
5#include "ccf/ds/nonstd.h"
7
8#include <algorithm>
9#include <array>
10#include <cstring>
11#include <fcntl.h>
12#include <openssl/crypto.h>
13#include <stdint.h>
14#include <sys/ioctl.h>
15#include <sys/types.h>
16#include <unistd.h>
17
18// Based on the SEV-SNP ABI Spec document at
19// https://www.amd.com/system/files/TechDocs/56860.pdf
20
21/* linux kernel 6.* versions of the ioctls that talk to the PSP */
22
24{
25 constexpr auto DEVICE = "/dev/sev-guest";
26
27#pragma pack(push, 1)
28 // Helper to add padding to a struct, so that the resulting struct has some
29 // minimum size. As a minor detail, the padding will be initialised to 0.
30 template <typename T, size_t N>
31 struct PaddedTo : public T
32 {
33 static_assert(
34 sizeof(T) < N, "No padding possible - struct is already N bytes");
35 static constexpr size_t num_padding_bytes = N - sizeof(T);
36 uint8_t padding[num_padding_bytes] = {0};
37 };
38
39 // Helper which surrounds a struct with some sentinel bytes, to aid detection
40 // of out-of-bounds writes.
41 template <typename T>
43 {
44 static constexpr size_t num_sentinel_bytes = 1024;
45
46 static constexpr uint8_t default_sentinel = 0x42;
47
48 static constexpr uint8_t pre_sentinel_first = 0xAA;
49 static constexpr uint8_t pre_sentinel_last = 0xBB;
50
51 static constexpr uint8_t post_sentinel_first = 0xCC;
52 static constexpr uint8_t post_sentinel_last = 0xDD;
53
57
68
69 bool sentinels_intact() const
70 {
72 {
73 return false;
74 }
76 {
77 return false;
78 }
79
81 {
82 return false;
83 }
85 {
86 return false;
87 }
88
89 return std::all_of(
90 std::next(std::begin(pre_sentinels)),
91 std::prev(std::end(pre_sentinels)),
92 [](uint8_t e) { return e == default_sentinel; }) &&
93 std::all_of(
94 std::next(std::begin(post_sentinels)),
95 std::prev(std::end(post_sentinels)),
96
97 [](uint8_t e) { return e == default_sentinel; });
98 }
99 };
100#pragma pack(pop)
101
102 // Table 22
103#pragma pack(push, 1)
105 {
106 uint8_t report_data[snp_attestation_report_data_size];
107 uint32_t vmpl = 0;
108 uint8_t reserved[28] = {0};
109 }; // snp_report_req in (linux) include/uapi/linux/sev-guest.h
110#pragma pack(pop)
111
112 // Table 25
113#pragma pack(push, 1)
115 {
116 uint32_t status;
117 uint32_t report_size;
118 uint8_t reserved[0x20 - 0x8];
120 uint8_t padding[64];
121 // padding to the size of SEV_SNP_REPORT_RSP_BUF_SZ (i.e., 1280 bytes)
122 };
123#pragma pack(pop)
124
125 // Table 20 of the SEVSNP ABI
126 constexpr uint8_t GUEST_FIELD_SELECT_GUEST_POLICY = 0b00000001;
127 constexpr uint8_t GUEST_FIELD_SELECT_IMAGE_ID = 0b00000010;
128 constexpr uint8_t GUEST_FIELD_SELECT_FAMILY_ID = 0b00000100;
129 constexpr uint8_t GUEST_FIELD_SELECT_MEASUREMENT = 0b00001000;
130 constexpr uint8_t GUEST_FIELD_SELECT_GUEST_SVN = 0b00010000;
131 constexpr uint8_t GUEST_FIELD_SELECT_TCB_VERSION = 0b00100000;
132
133#pragma pack(push, 1)
135 {
136 uint32_t key_select = 0;
137 uint32_t reserved = 0;
138 uint64_t guest_field_select = 0;
139 uint32_t vmpl = 0;
140 uint32_t guest_svn = 0;
142 }; // snp_derived_key_req in (linux) include/uapi/linux/sev-guest.h
143#pragma pack(pop)
144 static_assert(
145 sizeof(DerivedKeyReq) == 0x20,
146 "DerivedKeyReq struct size does not match expected size of 32 bytes");
147
148// Table 21
149#pragma pack(push, 1)
151 {
152 uint32_t status;
153 uint8_t reserved[0x20 - 0x04];
154 uint8_t data[32];
155 }; // snp_derived_key_req in (linux) include/uapi/linux/sev-guest.h
156#pragma pack(pop)
157
159 {
160 uint32_t fw;
161 uint32_t vmm;
162 };
163
165 {
166 uint64_t whole;
168 };
169
170 // https://www.kernel.org/doc/html/v6.4/virt/coco/sev-guest.html#api-description
171 template <typename Req, typename Resp>
173 {
174 /* Message version number */
175 uint32_t msg_version = 1;
176
177 /* Request and response structure address */
180
181 /* bits[63:32]: VMM error code, bits[31:0] firmware error code (see
182 * psp-sev.h) */
184 };
185
186 // This 4000 comes from the definition of snp_report_resp in
187 // https://github.com/torvalds/linux/blob/master/include/uapi/linux/sev-guest.h
190
195
196 // From linux/include/uapi/linux/sev-guest.h
197 constexpr char SEV_GUEST_IOC_TYPE = 'S';
202
203 static inline bool supports_sev_snp()
204 {
205 return access(DEVICE, W_OK) == 0;
206 }
207
209 {
210 IoctlSentinel<PaddedAttestationResp> resp_with_sentinel = {};
211 PaddedAttestationResp& padded_resp = resp_with_sentinel.data;
212
213 public:
215 {
216 AttestationReq req = {};
217 if (report_data.data.size() <= snp_attestation_report_data_size)
218 {
219 std::copy(
220 report_data.data.begin(), report_data.data.end(), req.report_data);
221 }
222 else
223 {
224 throw std::logic_error(
225 "User-defined report data is larger than available space");
226 }
227
228 int fd = open(DEVICE, O_RDWR | O_CLOEXEC);
229 if (fd < 0)
230 {
231 throw std::logic_error(
232 fmt::format("Failed to open \"{}\" ({})", DEVICE, fd));
233 }
234 auto close_guard = nonstd::make_close_fd_guard(&fd);
235
236 // Documented at
237 // https://www.kernel.org/doc/html/latest/virt/coco/sev-guest.html
238 GuestRequestAttestation payload = {
239 .req_data = &req, .resp_wrapper = &padded_resp, .exit_info = {0}};
240
241 int rc = ioctl(fd, SEV_SNP_GUEST_MSG_REPORT, &payload);
242 if (rc < 0)
243 {
244 const auto msg = fmt::format(
245 "Failed to issue ioctl SEV_SNP_GUEST_MSG_REPORT: {} fw_error: {} "
246 "vmm_error: {}",
247 strerror(errno),
248 payload.exit_info.errors.fw,
249 payload.exit_info.errors.vmm);
250 throw std::logic_error(msg);
251 }
252
253 if (!resp_with_sentinel.sentinels_intact())
254 {
255 // This occurs if a kernel/firmware upgrade causes the response to
256 // overflow our struct. If that happens, it is better to fail early than
257 // deal with memory corruption.
258 throw std::logic_error(
259 "SEV_SNP_GUEST_MSG_REPORT IOCTL overwrote safety sentinels.");
260 }
261 }
262
263 const snp::Attestation& get() const override
264 {
265 return padded_resp.report;
266 }
267
268 std::vector<uint8_t> get_raw() override
269 {
270 auto quote_bytes = reinterpret_cast<uint8_t*>(&padded_resp.report);
271 return {quote_bytes, quote_bytes + padded_resp.report_size};
272 }
273 };
274
276 {
277 IoctlSentinel<PaddedDerivedKeyResp> resp_with_sentinel;
278 PaddedDerivedKeyResp& padded_resp = resp_with_sentinel.data;
279
280 public:
281 DerivedKey(const TcbVersionRaw tcb = {})
282 {
283 int fd = open(DEVICE, O_RDWR | O_CLOEXEC);
284 if (fd < 0)
285 {
286 throw std::logic_error(
287 fmt::format("Failed to open \"{}\" ({})", DEVICE, fd));
288 }
289 auto close_guard = nonstd::make_close_fd_guard(&fd);
290
291 // This req by default mixes in HostData and the CPU VCEK
292 DerivedKeyReq req = {};
293
294 req.guest_field_select =
296 req.tcb_version = tcb;
297
298 GuestRequestDerivedKey payload = {
299 .req_data = &req, .resp_wrapper = &padded_resp, .exit_info = {0}};
300 int rc = ioctl(fd, SEV_SNP_GUEST_MSG_DERIVED_KEY, &payload);
301 if (rc < 0)
302 {
303 const auto msg = fmt::format(
304 "Failed to issue ioctl SEV_SNP_GUEST_MSG_DERIVED_KEY: {} fw_error: "
305 "{} vmm_error: {}",
306 strerror(errno),
307 payload.exit_info.errors.fw,
308 payload.exit_info.errors.vmm);
309 throw std::logic_error(msg);
310 }
311
312 if (!resp_with_sentinel.sentinels_intact())
313 {
314 // This occurs if a kernel/firmware upgrade causes the response to
315 // overflow our struct. If that happens, it is better to fail early than
316 // deal with memory corruption.
317 throw std::logic_error(
318 "SEV_SNP_GUEST_MSG_DERIVED_KEY IOCTL overwrote safety sentinels.");
319 }
320
321 if (padded_resp.status != 0)
322 {
323 const auto msg = fmt::format(
324 "Failed to issue ioctl SEV_SNP_GUEST_MSG_DERIVED_KEY: {}",
325 padded_resp.status);
326 throw std::logic_error(msg);
327 }
328 }
329
331 {
332 OPENSSL_cleanse(padded_resp.data, sizeof(padded_resp.data));
333 }
334
335 std::span<const uint8_t> get_raw()
336 {
337 return std::span<const uint8_t>{padded_resp.data};
338 }
339 };
340}
Definition attestation_sev_snp.h:543
Definition snp_ioctl6.h:209
Attestation(const PlatformAttestationReportData &report_data)
Definition snp_ioctl6.h:214
std::vector< uint8_t > get_raw() override
Definition snp_ioctl6.h:268
const snp::Attestation & get() const override
Definition snp_ioctl6.h:263
Definition snp_ioctl6.h:276
~DerivedKey()
Definition snp_ioctl6.h:330
std::span< const uint8_t > get_raw()
Definition snp_ioctl6.h:335
DerivedKey(const TcbVersionRaw tcb={})
Definition snp_ioctl6.h:281
Definition snp_ioctl6.h:24
constexpr char SEV_GUEST_IOC_TYPE
Definition snp_ioctl6.h:197
constexpr uint8_t GUEST_FIELD_SELECT_FAMILY_ID
Definition snp_ioctl6.h:128
GuestRequest< DerivedKeyReq, PaddedDerivedKeyResp > GuestRequestDerivedKey
Definition snp_ioctl6.h:194
constexpr int SEV_SNP_GUEST_MSG_REPORT
Definition snp_ioctl6.h:198
constexpr uint8_t GUEST_FIELD_SELECT_GUEST_SVN
Definition snp_ioctl6.h:130
constexpr uint8_t GUEST_FIELD_SELECT_IMAGE_ID
Definition snp_ioctl6.h:127
constexpr int SEV_SNP_GUEST_MSG_DERIVED_KEY
Definition snp_ioctl6.h:200
constexpr uint8_t GUEST_FIELD_SELECT_GUEST_POLICY
Definition snp_ioctl6.h:126
constexpr uint8_t GUEST_FIELD_SELECT_TCB_VERSION
Definition snp_ioctl6.h:131
constexpr auto DEVICE
Definition snp_ioctl6.h:25
constexpr uint8_t GUEST_FIELD_SELECT_MEASUREMENT
Definition snp_ioctl6.h:129
Definition report_data.h:56
std::vector< uint8_t > data
Definition report_data.h:57
Definition attestation_sev_snp.h:361
Definition attestation_sev_snp.h:199
Definition snp_ioctl6.h:105
uint8_t report_data[snp_attestation_report_data_size]
Definition snp_ioctl6.h:106
uint8_t reserved[28]
Definition snp_ioctl6.h:108
uint32_t vmpl
Definition snp_ioctl6.h:107
Definition snp_ioctl6.h:115
uint32_t status
Definition snp_ioctl6.h:116
uint32_t report_size
Definition snp_ioctl6.h:117
uint8_t padding[64]
Definition snp_ioctl6.h:120
struct Attestation report
Definition snp_ioctl6.h:119
uint8_t reserved[0x20 - 0x8]
Definition snp_ioctl6.h:118
Definition snp_ioctl6.h:135
uint64_t guest_field_select
Definition snp_ioctl6.h:138
uint32_t key_select
Definition snp_ioctl6.h:136
TcbVersionRaw tcb_version
Definition snp_ioctl6.h:141
uint32_t vmpl
Definition snp_ioctl6.h:139
uint32_t reserved
Definition snp_ioctl6.h:137
uint32_t guest_svn
Definition snp_ioctl6.h:140
Definition snp_ioctl6.h:151
uint8_t data[32]
Definition snp_ioctl6.h:154
uint32_t status
Definition snp_ioctl6.h:152
uint8_t reserved[0x20 - 0x04]
Definition snp_ioctl6.h:153
Definition snp_ioctl6.h:159
uint32_t fw
Definition snp_ioctl6.h:160
uint32_t vmm
Definition snp_ioctl6.h:161
Definition snp_ioctl6.h:173
Resp * resp_wrapper
Definition snp_ioctl6.h:179
ExitInfo exit_info
Definition snp_ioctl6.h:183
Req * req_data
Definition snp_ioctl6.h:178
uint32_t msg_version
Definition snp_ioctl6.h:175
Definition snp_ioctl6.h:43
uint8_t post_sentinels[num_sentinel_bytes]
Definition snp_ioctl6.h:56
static constexpr uint8_t post_sentinel_first
Definition snp_ioctl6.h:51
static constexpr size_t num_sentinel_bytes
Definition snp_ioctl6.h:44
static constexpr uint8_t pre_sentinel_first
Definition snp_ioctl6.h:48
IoctlSentinel()
Definition snp_ioctl6.h:58
static constexpr uint8_t post_sentinel_last
Definition snp_ioctl6.h:52
static constexpr uint8_t default_sentinel
Definition snp_ioctl6.h:46
T data
Definition snp_ioctl6.h:55
uint8_t pre_sentinels[num_sentinel_bytes]
Definition snp_ioctl6.h:54
bool sentinels_intact() const
Definition snp_ioctl6.h:69
static constexpr uint8_t pre_sentinel_last
Definition snp_ioctl6.h:49
Definition snp_ioctl6.h:32
uint8_t padding[num_padding_bytes]
Definition snp_ioctl6.h:36
static constexpr size_t num_padding_bytes
Definition snp_ioctl6.h:35
Definition snp_ioctl6.h:165
ExitInfoErrors errors
Definition snp_ioctl6.h:167
uint64_t whole
Definition snp_ioctl6.h:166