sev_guest_device/
ioctl.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! The module implements Linux SEV-SNP Guest APIs based on ioctl.
5
6// UNSAFETY: unsafe needed to make ioctl calls.
7#![expect(unsafe_code)]
8
9use crate::protocol;
10use std::fs::File;
11use std::os::fd::AsRawFd;
12use thiserror::Error;
13use zerocopy::FromZeros;
14use zerocopy::IntoBytes;
15
16#[expect(missing_docs)] // self-explanatory fields
17#[derive(Debug, Error)]
18pub enum Error {
19    #[error("failed to open /dev/sev-guest")]
20    OpenDevSevGuest(#[source] std::io::Error),
21    #[error("SNP_GET_REPORT ioctl failed")]
22    SnpGetReportIoctl(#[source] nix::Error),
23    #[error("SNP_GET_DERIVED_KEY ioctl failed")]
24    SnpGetDerivedKeyIoctl(#[source] nix::Error),
25}
26
27nix::ioctl_readwrite!(
28    /// `SNP_GET_REPORT` ioctl defined by Linux.
29    snp_get_report,
30    protocol::SNP_GUEST_REQ_IOC_TYPE,
31    0x0,
32    protocol::SnpGuestRequestIoctl
33);
34
35nix::ioctl_readwrite!(
36    /// `SNP_GET_DERIVED_KEY` ioctl defined by Linux.
37    snp_get_derived_key,
38    protocol::SNP_GUEST_REQ_IOC_TYPE,
39    0x1,
40    protocol::SnpGuestRequestIoctl
41);
42
43/// Abstraction of the /dev/sev-guest device.
44pub struct SevGuestDevice {
45    file: File,
46}
47
48impl SevGuestDevice {
49    /// Open an /dev/sev-guest device
50    pub fn open() -> Result<Self, Error> {
51        let sev_guest = std::fs::OpenOptions::new()
52            .read(true)
53            .write(true)
54            .open("/dev/sev-guest")
55            .map_err(Error::OpenDevSevGuest)?;
56
57        Ok(Self { file: sev_guest })
58    }
59
60    /// Invoke the `SNP_GET_REPORT` ioctl via the device.
61    pub fn get_report(&self, user_data: [u8; 64], vmpl: u32) -> Result<protocol::SnpReport, Error> {
62        let req = protocol::SnpReportReq {
63            user_data,
64            vmpl,
65            rsvd: [0u8; 28],
66        };
67
68        let resp = protocol::SnpReportResp::new_zeroed();
69
70        let mut snp_guest_request = protocol::SnpGuestRequestIoctl {
71            msg_version: protocol::SNP_GUEST_REQ_MSG_VERSION,
72            req_data: req.as_bytes().as_ptr() as u64,
73            resp_data: resp.as_bytes().as_ptr() as u64,
74            exitinfo: protocol::VmmErrorCode::new_zeroed(),
75        };
76
77        // SAFETY: Make SNP_GET_REPORT ioctl call to the device with correct types.
78        unsafe {
79            snp_get_report(self.file.as_raw_fd(), &mut snp_guest_request)
80                .map_err(Error::SnpGetReportIoctl)?;
81        }
82
83        Ok(resp.report)
84    }
85
86    /// Invoke the `SNP_GET_DERIVED_KEY` ioctl via the device.
87    pub fn get_derived_key(
88        &self,
89        root_key_select: u32,
90        guest_field_select: u64,
91        vmpl: u32,
92        guest_svn: u32,
93        tcb_version: u64,
94    ) -> Result<[u8; protocol::SNP_DERIVED_KEY_SIZE], Error> {
95        let req = protocol::SnpDerivedKeyReq {
96            root_key_select,
97            rsvd: 0u32,
98            guest_field_select,
99            vmpl,
100            guest_svn,
101            tcb_version,
102        };
103
104        let resp = protocol::SnpDerivedKeyResp::new_zeroed();
105
106        let mut snp_guest_request = protocol::SnpGuestRequestIoctl {
107            msg_version: protocol::SNP_GUEST_REQ_MSG_VERSION,
108            req_data: req.as_bytes().as_ptr() as u64,
109            resp_data: resp.as_bytes().as_ptr() as u64,
110            exitinfo: protocol::VmmErrorCode::new_zeroed(),
111        };
112
113        // SAFETY: Make SNP_GET_DERIVED_KEY ioctl call to the device with correct types
114        unsafe {
115            snp_get_derived_key(self.file.as_raw_fd(), &mut snp_guest_request)
116                .map_err(Error::SnpGetReportIoctl)?;
117        }
118
119        Ok(resp.derived_key)
120    }
121}