microsoft/openvmm

Public

mirrored fromhttps://github.com/microsoft/openvmmAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
2c4d0aeb7dba2fb3f768940958829289510f5001

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

openhcl/hcl/src/ioctl/deferred.rs

192lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Support routines for deferred actions.
5
6use super::Hcl;
7use crate::protocol;
8use crate::protocol::hcl_run;
9use cvm_tracing::CVM_ALLOWED;
10use std::cell::Cell;
11use std::cell::UnsafeCell;
12use std::marker::PhantomData;
13use zerocopy::IntoBytes;
14
15thread_local! {
16 static DEFERRED_ACTIONS: DeferredActions = const { DeferredActions::new() };
17}
18
19struct DeferredActions {
20 actions: [Cell<DeferredAction>; MAX_ACTIONS as usize],
21 used: Cell<u8>,
22}
23
24const MAX_ACTIONS: u8 = 8;
25const DISABLED: u8 = !0;
26
27impl DeferredActions {
28 const fn new() -> Self {
29 Self {
30 actions: [const { Cell::new(DeferredAction::Noop) }; MAX_ACTIONS as usize],
31 used: Cell::new(DISABLED),
32 }
33 }
34
35 fn drain(&self) -> &[Cell<DeferredAction>] {
36 let used = self.used.replace(0);
37 &self.actions[..used as usize]
38 }
39}
40
41/// Pushes an action to the current thread's list of deferred actions. If the
42/// list is full or there is no list for the current thread, the action will be
43/// run immediately.
44pub fn push_deferred_action(hcl: &Hcl, action: DeferredAction) {
45 DEFERRED_ACTIONS.with(|deferred| {
46 let used = deferred.used.get();
47 if used < MAX_ACTIONS {
48 deferred.actions[used as usize].set(action);
49 deferred.used.set(used + 1);
50 } else {
51 // The action couldn't be deferred, so run it immediately.
52 action.run(hcl)
53 }
54 });
55}
56
57/// A token representing that a deferred actions list has been registered for
58/// the current thread.
59///
60/// When dropped, this will flush any deferred actions that were registered. The
61/// owner can also call `flush` to run the actions immediately, if desired, and
62/// `move_to_slots` to copy the actions to the HCL run structure's action slots
63/// before running the VP.
64//
65// DEVNOTE: Use a PhantomData to ensure this isn't `Sync` or `Send`, so that it
66// doesn't move to another thread.
67pub struct RegisteredDeferredActions<'a>(&'a Hcl, PhantomData<*const ()>);
68
69/// Registers a deferred actions list for the current thread.
70pub fn register_deferred_actions(hcl: &Hcl) -> RegisteredDeferredActions<'_> {
71 DEFERRED_ACTIONS.with(|deferred| {
72 assert_eq!(deferred.used.replace(0), DISABLED);
73 });
74 RegisteredDeferredActions(hcl, PhantomData)
75}
76
77impl RegisteredDeferredActions<'_> {
78 /// Moves the queued actions to the slots in the run page. Issues any
79 /// immediately that won't fit in the run page.
80 pub fn move_to_slots(&mut self, slots: &mut DeferredActionSlots<'_>) {
81 self.with(|deferred, hcl| {
82 for action in deferred.drain() {
83 let action = action.get();
84 if !action.post(slots) {
85 action.run(hcl);
86 }
87 }
88 })
89 }
90
91 /// Runs actions immediately without deferring them to VTL return.
92 pub fn flush(&mut self) {
93 self.with(|deferred, hcl| {
94 for action in deferred.drain() {
95 action.get().run(hcl);
96 }
97 });
98 }
99
100 fn with(&mut self, f: impl FnOnce(&DeferredActions, &Hcl)) {
101 DEFERRED_ACTIONS.with(|deferred| {
102 debug_assert!(deferred.used.get() <= MAX_ACTIONS);
103 f(deferred, self.0);
104 });
105 }
106}
107
108impl Drop for RegisteredDeferredActions<'_> {
109 fn drop(&mut self) {
110 self.flush();
111 DEFERRED_ACTIONS.with(|deferred| {
112 assert_eq!(deferred.used.replace(DISABLED), 0);
113 })
114 }
115}
116
117/// A deferred action that can be handled by the hypervisor as part of switching
118/// VTLs.
119#[derive(Debug, Copy, Clone)]
120pub(crate) enum DeferredAction {
121 Noop,
122 SignalEvent { vp: u32, sint: u8, flag: u16 },
123}
124
125impl DeferredAction {
126 /// Run the action via a hypercall.
127 fn run(&self, hcl: &Hcl) {
128 match *self {
129 DeferredAction::Noop => {}
130 DeferredAction::SignalEvent { vp, sint, flag } => {
131 if let Err(err) = hcl.hvcall_signal_event_direct(vp, sint, flag) {
132 tracelimit::warn_ratelimited!(
133 CVM_ALLOWED,
134 error = &err as &dyn std::error::Error,
135 vp,
136 sint,
137 flag,
138 "failed to signal event"
139 );
140 }
141 }
142 }
143 }
144
145 /// Post the action to the HCL.
146 fn post(&self, slots: &mut DeferredActionSlots<'_>) -> bool {
147 match *self {
148 DeferredAction::Noop => true,
149 DeferredAction::SignalEvent { vp, sint, flag } => slots.push(
150 protocol::hv_vp_assist_page_signal_event {
151 action_type: protocol::HV_VP_ASSIST_PAGE_ACTION_TYPE_SIGNAL_EVENT,
152 vp,
153 vtl: 0,
154 sint,
155 flag,
156 }
157 .as_bytes(),
158 ),
159 }
160 }
161}
162
163/// A reference to the HCL run data structure's deferred action slots.
164pub(crate) struct DeferredActionSlots<'a>(&'a UnsafeCell<hcl_run>);
165
166impl<'a> DeferredActionSlots<'a> {
167 /// # Safety
168 /// The caller must ensure that the return action fields in `run` remain
169 /// valid and unaliased for the lifetime of this object.
170 pub unsafe fn new(run: &'a UnsafeCell<hcl_run>) -> Self {
171 Self(run)
172 }
173
174 fn push(&mut self, action: &[u8]) -> bool {
175 let (used, buffer);
176 // SAFETY: this thread is the only one concurrently accessing the
177 // action-related portions of the run structure.
178 unsafe {
179 used = &mut (*self.0.get()).vtl_ret_action_size;
180 buffer = &mut (*self.0.get()).vtl_ret_actions;
181 }
182 let offset = *used as usize;
183 if let Some(buffer) = buffer.get_mut(offset..offset + action.len()) {
184 buffer.copy_from_slice(action);
185 *used += action.len() as u32;
186 true
187 } else {
188 // The action buffer is full.
189 false
190 }
191 }
192}
193