Skip to content

Commit 0bdc9df

Browse files
committed
vtpm: Add pluggable storage trait for sealed vTPM state
Introduces SealedBlobStore — a backend-agnostic trait for persisting the sealed vTPM state blob across cold boots. Three bundled implementations: * StaticBufStore — in-CVM static buffer. Survives warm reboots within a single CVM lifetime; zeroed on cold boot. Suitable for development and bring-up. * IgvmVarStore — stub for an IGVM variable-backed store (production target; load/save plumbing TBD). * VsockHostStore — AF_VSOCK to a host-side persistence helper (feature-gated 'vsock'). Two ports separate the load and save directions so the helper can implement them as independent services. The trait is intentionally backend-agnostic to align with the upcoming COCONUT-SVSM block-layer abstraction and any KBS-stateful TPM service. Signed-off-by: Goodleon-Y <goodleon3@126.com>
1 parent 6c08cd2 commit 0bdc9df

2 files changed

Lines changed: 208 additions & 0 deletions

File tree

kernel/src/vtpm/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
/// TPM 2.0 command construction over a pluggable transport (used to proxy
1111
/// commands to a TPM endpoint outside the CVM).
1212
pub mod proxy;
13+
/// Pluggable storage backend for the sealed vTPM blob.
14+
pub mod sealed_store;
1315
/// TPM 2.0 Reference Implementation
1416
pub mod tcgtpm;
1517

kernel/src/vtpm/sealed_store.rs

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
// SPDX-License-Identifier: MIT
2+
//
3+
// Pluggable storage backend for the sealed vTPM blob.
4+
//
5+
// The vTPM persistence cycle (`Provision` → seal → store, store → load →
6+
// `Recover`) is intentionally decoupled from the underlying durable medium
7+
// so that the same crypto path can be paired with different host-side
8+
// storage strategies. Three backends are bundled:
9+
//
10+
// * `StaticBufStore` — in-CVM static buffer, survives warm reboots
11+
// within the lifetime of the CVM (test / dev).
12+
// * `IgvmVarStore` — IGVM variable interface (stubbed, TODO).
13+
// * `VsockHostStore` — AF_VSOCK to a host-side helper that persists
14+
// the blob on the host filesystem
15+
// (feature-gated `vsock`).
16+
//
17+
// The trait is intentionally backend-agnostic so that it can be paired
18+
// with the upcoming SVSM block-layer abstraction or with a KBS-backed
19+
// stateful vTPM service without changing the persistence logic itself.
20+
21+
extern crate alloc;
22+
23+
use alloc::vec::Vec;
24+
25+
use crate::locking::SpinLock;
26+
use crate::protocols::errors::SvsmReqError;
27+
28+
/// Persistent storage backend for the sealed vTPM blob.
29+
///
30+
/// Implementations must be safe to share across CPUs (`Send + Sync`);
31+
/// concurrency control is the implementation's responsibility.
32+
pub trait SealedBlobStore: Send + Sync {
33+
/// Return the previously saved blob, if any.
34+
///
35+
/// `Ok(None)` indicates a cold boot or an empty backend; the caller
36+
/// should fall back to `VtpmBootMode::Provision`. A backend error is
37+
/// distinct from an empty backend and is reported via `Err`.
38+
fn load(&self) -> Result<Option<Vec<u8>>, SvsmReqError>;
39+
40+
/// Persist the given blob.
41+
///
42+
/// Implementations should overwrite any prior content atomically when
43+
/// the backend supports it.
44+
fn save(&self, blob: &[u8]) -> Result<(), SvsmReqError>;
45+
}
46+
47+
// ============================================================
48+
// StaticBufStore — in-CVM static buffer
49+
// ============================================================
50+
51+
const STATIC_BUF_CAPACITY: usize = 4096;
52+
53+
#[derive(Debug)]
54+
struct StaticBuf {
55+
buf: [u8; STATIC_BUF_CAPACITY],
56+
len: usize,
57+
valid: bool,
58+
}
59+
60+
/// In-CVM static buffer. Survives warm reboots within a single CVM
61+
/// lifetime; zeroed on cold boot. Intended for development, testing,
62+
/// and bring-up scenarios where no durable backing store is available.
63+
#[derive(Debug)]
64+
pub struct StaticBufStore {
65+
inner: SpinLock<StaticBuf>,
66+
}
67+
68+
impl StaticBufStore {
69+
/// Create an empty store. Suitable for declaring as a `static`.
70+
pub const fn new() -> Self {
71+
Self {
72+
inner: SpinLock::new(StaticBuf {
73+
buf: [0u8; STATIC_BUF_CAPACITY],
74+
len: 0,
75+
valid: false,
76+
}),
77+
}
78+
}
79+
80+
/// Storage capacity in bytes.
81+
pub const fn capacity(&self) -> usize {
82+
STATIC_BUF_CAPACITY
83+
}
84+
}
85+
86+
impl Default for StaticBufStore {
87+
fn default() -> Self {
88+
Self::new()
89+
}
90+
}
91+
92+
impl SealedBlobStore for StaticBufStore {
93+
fn load(&self) -> Result<Option<Vec<u8>>, SvsmReqError> {
94+
let g = self.inner.lock();
95+
if g.valid && g.len > 0 {
96+
Ok(Some(g.buf[..g.len].to_vec()))
97+
} else {
98+
Ok(None)
99+
}
100+
}
101+
102+
fn save(&self, blob: &[u8]) -> Result<(), SvsmReqError> {
103+
if blob.len() > STATIC_BUF_CAPACITY {
104+
log::error!(
105+
"StaticBufStore::save — blob {} exceeds capacity {}",
106+
blob.len(),
107+
STATIC_BUF_CAPACITY
108+
);
109+
return Err(SvsmReqError::invalid_request());
110+
}
111+
let mut g = self.inner.lock();
112+
g.buf[..blob.len()].copy_from_slice(blob);
113+
g.len = blob.len();
114+
g.valid = true;
115+
Ok(())
116+
}
117+
}
118+
119+
// ============================================================
120+
// IgvmVarStore — stub for future IGVM variable backend
121+
// ============================================================
122+
123+
/// IGVM variable-backed store. Reserved for the production path; the
124+
/// load/save plumbing through the IGVM variable interface is not yet
125+
/// implemented in this tree.
126+
#[derive(Debug)]
127+
pub struct IgvmVarStore;
128+
129+
impl IgvmVarStore {
130+
pub const fn new() -> Self {
131+
Self
132+
}
133+
}
134+
135+
impl Default for IgvmVarStore {
136+
fn default() -> Self {
137+
Self::new()
138+
}
139+
}
140+
141+
impl SealedBlobStore for IgvmVarStore {
142+
fn load(&self) -> Result<Option<Vec<u8>>, SvsmReqError> {
143+
log::debug!("IgvmVarStore::load — not yet implemented, returning empty");
144+
Ok(None)
145+
}
146+
147+
fn save(&self, _blob: &[u8]) -> Result<(), SvsmReqError> {
148+
log::warn!("IgvmVarStore::save — not yet implemented, silently dropped");
149+
Ok(())
150+
}
151+
}
152+
153+
// ============================================================
154+
// VsockHostStore — AF_VSOCK to a host-side persistence helper
155+
// ============================================================
156+
157+
/// VSOCK-backed store. Persists the sealed blob via a host-side helper
158+
/// reachable over AF_VSOCK. The two ports separate the load and save
159+
/// directions so that the helper can implement them as independent
160+
/// services if desired.
161+
#[cfg(feature = "vsock")]
162+
#[derive(Debug)]
163+
pub struct VsockHostStore {
164+
pub cid: u32,
165+
pub load_port: u32,
166+
pub save_port: u32,
167+
}
168+
169+
#[cfg(feature = "vsock")]
170+
impl VsockHostStore {
171+
/// Default VSOCK ports: 9997 for load, 9998 for save.
172+
pub const DEFAULT_LOAD_PORT: u32 = 9997;
173+
pub const DEFAULT_SAVE_PORT: u32 = 9998;
174+
175+
pub const fn new(cid: u32, load_port: u32, save_port: u32) -> Self {
176+
Self {
177+
cid,
178+
load_port,
179+
save_port,
180+
}
181+
}
182+
}
183+
184+
#[cfg(feature = "vsock")]
185+
impl SealedBlobStore for VsockHostStore {
186+
fn load(&self) -> Result<Option<Vec<u8>>, SvsmReqError> {
187+
// TODO: open AF_VSOCK(cid, load_port), read framed blob.
188+
log::debug!(
189+
"VsockHostStore::load(cid={}, port={}) — not yet wired",
190+
self.cid,
191+
self.load_port
192+
);
193+
Ok(None)
194+
}
195+
196+
fn save(&self, blob: &[u8]) -> Result<(), SvsmReqError> {
197+
// TODO: open AF_VSOCK(cid, save_port), write framed blob.
198+
log::debug!(
199+
"VsockHostStore::save(cid={}, port={}, {} bytes) — not yet wired",
200+
self.cid,
201+
self.save_port,
202+
blob.len()
203+
);
204+
Ok(())
205+
}
206+
}

0 commit comments

Comments
 (0)