hv1_emulator/
synic.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Synthetic interrupt controller emulation.
5
6use crate::RequestInterrupt;
7use crate::VtlProtectAccess;
8use crate::pages::OverlayPage;
9use hvdef::HV_MESSAGE_SIZE;
10use hvdef::HV_PAGE_SIZE_USIZE;
11use hvdef::HvAllArchRegisterName;
12use hvdef::HvError;
13use hvdef::HvMessage;
14use hvdef::HvMessageHeader;
15use hvdef::HvMessageType;
16use hvdef::HvRegisterName;
17use hvdef::HvRegisterValue;
18use hvdef::HvRegisterVsmVina;
19use hvdef::HvResult;
20use hvdef::HvSynicSimpSiefp;
21use hvdef::HvSynicStimerConfig;
22use hvdef::NUM_SINTS;
23use hvdef::TimerMessagePayload;
24use inspect::Inspect;
25use parking_lot::RwLock;
26use safeatomic::AtomicSliceOps;
27use std::array;
28use std::sync::Arc;
29use std::sync::atomic::Ordering;
30use virt::x86::MsrError;
31use vm_topology::processor::VpIndex;
32use zerocopy::IntoBytes;
33
34/// The virtual processor synthetic interrupt controller state.
35#[derive(Inspect)]
36pub struct ProcessorSynic {
37    #[inspect(flatten)]
38    sints: SintState,
39    #[inspect(iter_by_index)]
40    timers: [Timer; hvdef::NUM_TIMERS],
41    #[inspect(skip)]
42    shared: Arc<RwLock<SharedProcessorState>>,
43    #[inspect(debug)]
44    vina: HvRegisterVsmVina,
45}
46
47#[derive(Inspect)]
48struct SintState {
49    #[inspect(hex, with = "|&x| u64::from(x)")]
50    siefp: HvSynicSimpSiefp,
51    #[inspect(hex, with = "|&x| u64::from(x)")]
52    simp: HvSynicSimpSiefp,
53    simp_page: OverlayPage,
54    #[inspect(hex, with = "|&x| u64::from(x)")]
55    scontrol: hvdef::HvSynicScontrol,
56    #[inspect(
57        hex,
58        with = "|x| inspect::iter_by_index(x).map_value(|x| u64::from(*x))"
59    )]
60    sint: [hvdef::HvSynicSint; NUM_SINTS],
61    pending_sints: u16,
62    ready_sints: u16,
63}
64
65impl Default for SintState {
66    fn default() -> Self {
67        Self {
68            siefp: HvSynicSimpSiefp::new(),
69            simp: HvSynicSimpSiefp::new(),
70            simp_page: OverlayPage::default(),
71            scontrol: hvdef::HvSynicScontrol::new().with_enabled(true),
72            sint: [hvdef::HvSynicSint::new().with_masked(true); NUM_SINTS],
73            pending_sints: 0,
74            ready_sints: 0,
75        }
76    }
77}
78
79#[derive(Default, Inspect)]
80struct Timer {
81    #[inspect(hex, with = "|&x| u64::from(x)")]
82    config: HvSynicStimerConfig,
83    count: u64,
84    reevaluate: bool,
85    due_time: Option<u64>,
86}
87
88#[derive(Inspect)]
89struct SharedProcessorState {
90    online: bool,
91    enabled: bool,
92    siefp_page: OverlayPage,
93    #[inspect(
94        hex,
95        with = "|x| inspect::iter_by_index(x).map_value(|x| u64::from(*x))"
96    )]
97    sint: [hvdef::HvSynicSint; NUM_SINTS],
98}
99
100impl SharedProcessorState {
101    fn new() -> Self {
102        Self {
103            online: false,
104            enabled: false,
105            siefp_page: OverlayPage::default(),
106            sint: [hvdef::HvSynicSint::new(); NUM_SINTS],
107        }
108    }
109
110    fn at_reset() -> Self {
111        Self {
112            online: true,
113            enabled: true,
114            siefp_page: OverlayPage::default(),
115            sint: [hvdef::HvSynicSint::new().with_masked(true); NUM_SINTS],
116        }
117    }
118}
119
120/// A partition-wide synthetic interrupt controller.
121#[derive(Inspect)]
122pub struct GlobalSynic {
123    #[inspect(iter_by_index)]
124    vps: Vec<Arc<RwLock<SharedProcessorState>>>,
125}
126
127fn sint_interrupt(request: &mut dyn RequestInterrupt, sint: hvdef::HvSynicSint) {
128    assert!(
129        !sint.masked() && !sint.proxy(),
130        "caller should have verified sint was ready"
131    );
132    if !sint.polling() {
133        request.request_interrupt(sint.vector().into(), sint.auto_eoi());
134    }
135}
136
137impl Timer {
138    fn evaluate(
139        &mut self,
140        timer_index: u32,
141        sints: &mut SintState,
142        ref_time_now: u64,
143        interrupt: &mut dyn RequestInterrupt,
144    ) -> Option<u64> {
145        if self.reevaluate {
146            self.reevaluate = false;
147            self.due_time = if self.config.enabled() && self.count != 0 {
148                Some(if self.config.periodic() {
149                    ref_time_now.wrapping_add(self.count)
150                } else {
151                    self.count
152                })
153            } else {
154                None
155            };
156        }
157
158        let due_time = self.due_time?;
159        let delta = due_time.wrapping_sub(ref_time_now);
160        if (delta as i64) > 0 {
161            return Some(due_time);
162        }
163
164        if self.config.direct_mode() {
165            interrupt.request_interrupt(self.config.apic_vector().into(), false);
166        } else {
167            let sint_index = self.config.sint();
168            let sint = sints.sint[sint_index as usize];
169            if sint.proxy()
170                || sint.masked()
171                || sints.pending_sints & (1 << sint_index) != 0
172                || !sints.check_sint_ready(sint_index)
173            {
174                return None;
175            }
176
177            let payload = TimerMessagePayload {
178                timer_index,
179                reserved: 0,
180                expiration_time: due_time,
181                delivery_time: ref_time_now,
182            };
183
184            let message = HvMessage::new(
185                HvMessageType::HvMessageTypeTimerExpired,
186                0,
187                payload.as_bytes(),
188            );
189
190            sints.post_message(sint_index, &message, &mut *interrupt);
191        }
192
193        // One-shot timers must be disabled upon expiration.
194        if !self.config.periodic() {
195            self.config.set_enabled(false);
196        }
197
198        self.due_time = if self.config.periodic() {
199            Some(ref_time_now.wrapping_add(self.count))
200        } else {
201            None
202        };
203
204        self.due_time
205    }
206}
207
208/// Error returned when trying to wait for readiness or signal events to a
209/// proxied SINT.
210pub struct SintProxied;
211
212impl GlobalSynic {
213    /// Returns a new instance of the synthetic interrupt controller.
214    pub fn new(max_vp_count: u32) -> Self {
215        Self {
216            vps: (0..max_vp_count)
217                .map(|_| Arc::new(RwLock::new(SharedProcessorState::new())))
218                .collect(),
219        }
220    }
221
222    /// Signals an event to the specified virtual processor.
223    ///
224    /// `interrupt` is called with the target APIC vector while holding a lock
225    /// preventing the synic state from changing.
226    ///
227    /// Returns `true` if the event flag is newly signaled.
228    pub fn signal_event(
229        &self,
230        vp: VpIndex,
231        sint_index: u8,
232        flag: u16,
233        interrupt: &mut dyn RequestInterrupt,
234    ) -> Result<bool, SintProxied> {
235        let Some(vp) = self.vps.get(vp.index() as usize) else {
236            return Ok(false);
237        };
238        let vp = vp.read();
239        let sint_index = sint_index as usize;
240        let sint = vp.sint[sint_index];
241        let flag = flag as usize;
242        if sint.proxy() {
243            return Err(SintProxied);
244        }
245        if !vp.enabled || sint.masked() {
246            return Ok(false);
247        }
248        let byte_offset = sint_index * (HV_PAGE_SIZE_USIZE / NUM_SINTS) + flag / 8;
249        let mask = 1 << (flag % 8);
250        if vp.siefp_page[byte_offset].fetch_or(mask, Ordering::SeqCst) & mask != 0 {
251            return Ok(false);
252        }
253        sint_interrupt(interrupt, sint);
254        Ok(true)
255    }
256
257    /// Adds a virtual processor to the synthetic interrupt controller state.
258    pub fn add_vp(&self, vp_index: VpIndex) -> ProcessorSynic {
259        let shared = self.vps[vp_index.index() as usize].clone();
260        let old_shared = std::mem::replace(&mut *shared.write(), SharedProcessorState::at_reset());
261        assert!(!old_shared.online);
262
263        ProcessorSynic {
264            sints: SintState::default(),
265            timers: array::from_fn(|_| Timer::default()),
266            shared,
267            vina: HvRegisterVsmVina::new(),
268        }
269    }
270}
271
272impl ProcessorSynic {
273    /// Resets the synic state back to its initial state.
274    pub fn reset(&mut self) {
275        let Self {
276            sints,
277            timers,
278            shared,
279            vina,
280        } = self;
281        *sints = SintState::default();
282        *timers = array::from_fn(|_| Timer::default());
283        *shared.write() = SharedProcessorState::at_reset();
284        *vina = HvRegisterVsmVina::new();
285    }
286
287    /// Returns the event flags page register.
288    pub fn siefp(&self) -> u64 {
289        self.sints.siefp.into()
290    }
291
292    /// Returns the message page register.
293    pub fn simp(&self) -> u64 {
294        self.sints.simp.into()
295    }
296
297    /// Returns the `SCONTROL` register.
298    pub fn scontrol(&self) -> u64 {
299        self.sints.scontrol.into()
300    }
301
302    /// Returns the `SVERSION` register.
303    pub fn sversion(&self) -> u64 {
304        1
305    }
306
307    /// Returns the end-of-message register.
308    pub fn eom(&self) -> u64 {
309        0
310    }
311
312    /// Returns the specified `SINT` register.
313    pub fn sint(&self, n: u8) -> u64 {
314        self.sints.sint[n as usize].into()
315    }
316
317    /// Returns the set of SINTs that are proxied to the host.
318    pub fn proxied_sints(&self) -> u16 {
319        self.sints
320            .sint
321            .iter()
322            .enumerate()
323            .fold(0, |v, (i, s)| v | ((s.proxy() as u16) << i))
324    }
325
326    /// Returns the specified synthetic timer configuration register.
327    pub fn stimer_config(&self, n: usize) -> u64 {
328        self.timers[n].config.into()
329    }
330
331    /// Returns the specified synthetic timer count register.
332    pub fn stimer_count(&self, n: usize) -> u64 {
333        self.timers[n].count
334    }
335
336    /// Returns the value of the VINA register.
337    pub fn vina(&self) -> HvRegisterVsmVina {
338        self.vina
339    }
340
341    /// Sets the event flags page register.
342    pub fn set_siefp(
343        &mut self,
344        v: u64,
345        prot_access: &mut dyn VtlProtectAccess,
346    ) -> Result<(), MsrError> {
347        let siefp = HvSynicSimpSiefp::from(v);
348        tracing::debug!(?siefp, "setting siefp");
349        let mut shared = self.shared.write();
350        if siefp.enabled()
351            && (!self.sints.siefp.enabled() || siefp.base_gpn() != self.sints.siefp.base_gpn())
352        {
353            shared
354                .siefp_page
355                .remap(siefp.base_gpn(), prot_access)
356                .map_err(|_| MsrError::InvalidAccess)?;
357        } else if !siefp.enabled() {
358            shared.siefp_page.unmap(prot_access);
359        }
360        self.sints.siefp = siefp;
361        Ok(())
362    }
363
364    /// Sets the message page register.
365    pub fn set_simp(
366        &mut self,
367        v: u64,
368        prot_access: &mut dyn VtlProtectAccess,
369    ) -> Result<(), MsrError> {
370        let simp = HvSynicSimpSiefp::from(v);
371        tracing::debug!(?simp, "setting simp");
372        if simp.enabled()
373            && (!self.sints.simp.enabled() || simp.base_gpn() != self.sints.simp.base_gpn())
374        {
375            self.sints
376                .simp_page
377                .remap(simp.base_gpn(), prot_access)
378                .map_err(|_| MsrError::InvalidAccess)?;
379        } else if !simp.enabled() {
380            self.sints.simp_page.unmap(prot_access);
381        }
382        self.sints.simp = simp;
383        Ok(())
384    }
385
386    /// Sets the `SCONTROL` register.
387    pub fn set_scontrol(&mut self, v: u64) {
388        self.sints.scontrol = v.into();
389        self.shared.write().enabled = self.sints.scontrol.enabled();
390    }
391
392    /// Performs an end-of-message operation.
393    pub fn set_eom(&mut self, _v: u64) {
394        // FUTURE: mark that the processor should scan message queues.
395    }
396
397    /// Sets the specified `SINT` register.
398    pub fn set_sint(&mut self, n: usize, v: u64) {
399        let sint = v.into();
400        self.sints.sint[n] = sint;
401        self.shared.write().sint[n] = sint;
402    }
403
404    /// Sets the specified synthetic timer configuration register.
405    pub fn set_stimer_config(&mut self, n: usize, v: u64) {
406        let config = v.into();
407        self.timers[n].config = config;
408        self.timers[n].reevaluate = true;
409    }
410
411    /// Sets the specified synthetic timer count register.
412    pub fn set_stimer_count(&mut self, n: usize, v: u64) {
413        self.timers[n].count = v;
414        if self.timers[n].config.auto_enable() && self.timers[n].count != 0 {
415            self.timers[n].config.set_enabled(true);
416        }
417
418        self.timers[n].reevaluate = true;
419    }
420
421    /// Requests a notification when any of the requested sints has a free
422    /// message slot.
423    ///
424    /// The free sints will be returned by [`Self::scan`].
425    pub fn request_sint_readiness(&mut self, sints: u16) {
426        self.sints.pending_sints = sints;
427    }
428
429    /// Writes a message to the message page.
430    ///
431    /// Returns `Err(HvError::ObjectInUse)` if the message slot is full.
432    pub fn post_message(
433        &mut self,
434        sint_index: u8,
435        message: &HvMessage,
436        interrupt: &mut dyn RequestInterrupt,
437    ) -> Result<(), HvError> {
438        let sint = &self.sints.sint[sint_index as usize];
439        if sint.masked() || sint.proxy() {
440            return Err(HvError::InvalidSynicState);
441        }
442        if self.sints.ready_sints & (1 << sint_index) == 0 {
443            return Err(HvError::ObjectInUse);
444        }
445
446        self.sints.post_message(sint_index, message, interrupt);
447
448        Ok(())
449    }
450
451    /// Writes an intercept message to the message page. Meant to be used on
452    /// paths where the message can be written directly without using the
453    /// message queues, as it marks the intercept sint as ready.
454    ///
455    /// Returns `Err(HvError::ObjectInUse)` if the message slot is full.
456    pub fn post_intercept_message(
457        &mut self,
458        message: &HvMessage,
459        interrupt: &mut dyn RequestInterrupt,
460    ) -> Result<(), HvError> {
461        if self
462            .sints
463            .check_sint_ready(hvdef::HV_SYNIC_INTERCEPTION_SINT_INDEX)
464        {
465            self.post_message(hvdef::HV_SYNIC_INTERCEPTION_SINT_INDEX, message, interrupt)
466        } else {
467            Err(HvError::ObjectInUse)
468        }
469    }
470
471    fn reg_to_msr(reg: HvRegisterName) -> HvResult<u32> {
472        Ok(match HvAllArchRegisterName(reg.0) {
473            HvAllArchRegisterName::Sint0
474            | HvAllArchRegisterName::Sint1
475            | HvAllArchRegisterName::Sint2
476            | HvAllArchRegisterName::Sint3
477            | HvAllArchRegisterName::Sint4
478            | HvAllArchRegisterName::Sint5
479            | HvAllArchRegisterName::Sint6
480            | HvAllArchRegisterName::Sint7
481            | HvAllArchRegisterName::Sint8
482            | HvAllArchRegisterName::Sint9
483            | HvAllArchRegisterName::Sint10
484            | HvAllArchRegisterName::Sint11
485            | HvAllArchRegisterName::Sint12
486            | HvAllArchRegisterName::Sint13
487            | HvAllArchRegisterName::Sint14
488            | HvAllArchRegisterName::Sint15 => {
489                hvdef::HV_X64_MSR_SINT0 + (reg.0 - HvAllArchRegisterName::Sint0.0)
490            }
491            HvAllArchRegisterName::Scontrol => hvdef::HV_X64_MSR_SCONTROL,
492            HvAllArchRegisterName::Sversion => hvdef::HV_X64_MSR_SVERSION,
493            HvAllArchRegisterName::Sifp => hvdef::HV_X64_MSR_SIEFP,
494            HvAllArchRegisterName::Sipp => hvdef::HV_X64_MSR_SIMP,
495            HvAllArchRegisterName::Eom => hvdef::HV_X64_MSR_EOM,
496            HvAllArchRegisterName::Stimer0Config
497            | HvAllArchRegisterName::Stimer0Count
498            | HvAllArchRegisterName::Stimer1Config
499            | HvAllArchRegisterName::Stimer1Count
500            | HvAllArchRegisterName::Stimer2Config
501            | HvAllArchRegisterName::Stimer2Count
502            | HvAllArchRegisterName::Stimer3Config
503            | HvAllArchRegisterName::Stimer3Count => {
504                hvdef::HV_X64_MSR_STIMER0_CONFIG + (reg.0 - HvAllArchRegisterName::Stimer0Config.0)
505            }
506            _ => {
507                tracelimit::error_ratelimited!(?reg, "unknown synic register");
508                return Err(HvError::UnknownRegisterName);
509            }
510        })
511    }
512
513    fn msrerr_to_hverr(err: MsrError) -> HvError {
514        match err {
515            MsrError::Unknown => HvError::UnknownRegisterName,
516            MsrError::InvalidAccess => HvError::InvalidParameter,
517        }
518    }
519
520    /// Writes a synthetic interrupt controller register.
521    pub fn write_reg(
522        &mut self,
523        reg: HvRegisterName,
524        v: HvRegisterValue,
525        prot_access: &mut dyn VtlProtectAccess,
526    ) -> HvResult<()> {
527        match HvAllArchRegisterName(reg.0) {
528            HvAllArchRegisterName::VsmVina => {
529                let v = HvRegisterVsmVina::from(v.as_u64());
530                if v.reserved() != 0 {
531                    return Err(HvError::InvalidParameter);
532                }
533                self.vina = v;
534            }
535            _ => self
536                .write_msr(Self::reg_to_msr(reg)?, v.as_u64(), prot_access)
537                .map_err(Self::msrerr_to_hverr)?,
538        }
539        Ok(())
540    }
541
542    /// Writes an x64 MSR.
543    pub fn write_msr(
544        &mut self,
545        msr: u32,
546        v: u64,
547        prot_access: &mut dyn VtlProtectAccess,
548    ) -> Result<(), MsrError> {
549        match msr {
550            msr @ hvdef::HV_X64_MSR_STIMER0_CONFIG..=hvdef::HV_X64_MSR_STIMER3_COUNT => {
551                let offset = msr - hvdef::HV_X64_MSR_STIMER0_CONFIG;
552                let timer = (offset >> 1) as _;
553                let is_count = offset & 1 != 0;
554                if is_count {
555                    self.set_stimer_count(timer, v);
556                } else {
557                    self.set_stimer_config(timer, v);
558                }
559            }
560            _ => self.write_nontimer_msr(msr, v, prot_access)?,
561        }
562        Ok(())
563    }
564
565    /// Writes a non-synthetic-timer x64 MSR.
566    pub fn write_nontimer_msr(
567        &mut self,
568        msr: u32,
569        v: u64,
570        prot_access: &mut dyn VtlProtectAccess,
571    ) -> Result<(), MsrError> {
572        match msr {
573            hvdef::HV_X64_MSR_SCONTROL => self.set_scontrol(v),
574            hvdef::HV_X64_MSR_SVERSION => return Err(MsrError::InvalidAccess),
575            hvdef::HV_X64_MSR_SIEFP => self
576                .set_siefp(v, prot_access)
577                .map_err(|_| MsrError::InvalidAccess)?,
578            hvdef::HV_X64_MSR_SIMP => self
579                .set_simp(v, prot_access)
580                .map_err(|_| MsrError::InvalidAccess)?,
581            hvdef::HV_X64_MSR_EOM => self.set_eom(v),
582            msr @ hvdef::HV_X64_MSR_SINT0..=hvdef::HV_X64_MSR_SINT15 => {
583                self.set_sint((msr - hvdef::HV_X64_MSR_SINT0) as usize, v)
584            }
585            _ => return Err(MsrError::Unknown),
586        }
587        Ok(())
588    }
589
590    /// Reads a synthetic interrupt controller register.
591    pub fn read_reg(&self, reg: HvRegisterName) -> HvResult<HvRegisterValue> {
592        let v = match HvAllArchRegisterName(reg.0) {
593            HvAllArchRegisterName::VsmVina => self.vina.into_bits(),
594            _ => self
595                .read_msr(Self::reg_to_msr(reg)?)
596                .map_err(Self::msrerr_to_hverr)?,
597        };
598        Ok(v.into())
599    }
600
601    /// Reads an x64 MSR.
602    pub fn read_msr(&self, msr: u32) -> Result<u64, MsrError> {
603        let value = match msr {
604            msr @ hvdef::HV_X64_MSR_STIMER0_CONFIG..=hvdef::HV_X64_MSR_STIMER3_COUNT => {
605                let offset = msr - hvdef::HV_X64_MSR_STIMER0_CONFIG;
606                let timer = (offset >> 1) as _;
607                let is_count = offset & 1 != 0;
608                if is_count {
609                    self.stimer_count(timer)
610                } else {
611                    self.stimer_config(timer)
612                }
613            }
614            _ => self.read_nontimer_msr(msr)?,
615        };
616        Ok(value)
617    }
618
619    /// Reads a non-synthetic-timer x64 MSR.
620    pub fn read_nontimer_msr(&self, msr: u32) -> Result<u64, MsrError> {
621        let value = match msr {
622            hvdef::HV_X64_MSR_SCONTROL => self.scontrol(),
623            hvdef::HV_X64_MSR_SVERSION => self.sversion(),
624            hvdef::HV_X64_MSR_SIEFP => self.siefp(),
625            hvdef::HV_X64_MSR_SIMP => self.simp(),
626            hvdef::HV_X64_MSR_EOM => self.eom(),
627            msr @ hvdef::HV_X64_MSR_SINT0..=hvdef::HV_X64_MSR_SINT15 => {
628                self.sint((msr - hvdef::HV_X64_MSR_SINT0) as u8)
629            }
630            _ => return Err(MsrError::Unknown),
631        };
632        Ok(value)
633    }
634
635    /// Scans for pending messages and timers.
636    ///
637    /// Calls `interrupt` with the APIC vector to signal, possibly multiple
638    /// times for different SINTs.
639    ///
640    /// Returns SINTs that are now deliverable after calls to
641    /// [`Self::request_sint_readiness`].
642    #[must_use]
643    pub fn scan(
644        &mut self,
645        ref_time_now: u64,
646        interrupt: &mut dyn RequestInterrupt,
647    ) -> (u16, Option<u64>) {
648        // Evaluate `ready_sints` for each pending SINT.
649        if self.sints.pending_sints != 0 {
650            for sint in 0..NUM_SINTS as u8 {
651                if self.sints.pending_sints & (1 << sint) != 0 {
652                    self.sints.check_sint_ready(sint);
653                }
654            }
655        }
656
657        // Scan for due timers.
658        let mut next_ref_time: Option<u64> = None;
659        if self
660            .timers
661            .iter()
662            .any(|t| t.reevaluate || t.due_time.is_some())
663        {
664            for (timer_index, timer) in self.timers.iter_mut().enumerate() {
665                if let Some(next) =
666                    timer.evaluate(timer_index as u32, &mut self.sints, ref_time_now, interrupt)
667                {
668                    match next_ref_time.as_mut() {
669                        Some(v) => *v = (*v).min(next),
670                        None => next_ref_time = Some(next),
671                    }
672                }
673            }
674        }
675
676        let ready_pending_sints = self.sints.ready_sints & self.sints.pending_sints;
677        self.sints.pending_sints &= !ready_pending_sints;
678        (ready_pending_sints, next_ref_time)
679    }
680}
681
682impl SintState {
683    fn post_message(
684        &mut self,
685        sint_index: u8,
686        message: &HvMessage,
687        interrupt: &mut dyn RequestInterrupt,
688    ) {
689        let sint = self.sint[sint_index as usize];
690
691        assert!(
692            !sint.masked() && !sint.proxy() && self.ready_sints & (1 << sint_index) != 0,
693            "caller should have verified sint was ready"
694        );
695
696        let offset = sint_index as usize * HV_MESSAGE_SIZE;
697        let data = message.as_bytes();
698        self.simp_page[offset..offset + data.len()].atomic_write(data);
699        self.ready_sints &= !(1 << sint_index);
700        sint_interrupt(interrupt, sint);
701    }
702
703    fn check_sint_ready(&mut self, sint: u8) -> bool {
704        if self.ready_sints & (1 << sint) != 0 {
705            return true;
706        }
707        let offset = sint as usize * HV_MESSAGE_SIZE;
708        let mut header: HvMessageHeader =
709            self.simp_page[offset..offset + size_of::<HvMessageHeader>()].atomic_read_obj();
710
711        if header.typ != HvMessageType::HvMessageTypeNone {
712            // The slot is full. Mark the message pending so that the guest forces an EOM.
713            if !header.flags.message_pending() {
714                header.flags.set_message_pending(true);
715                let data = header.as_bytes();
716                self.simp_page[offset..offset + data.len()].atomic_write(data);
717            }
718            return false;
719        }
720        self.ready_sints |= 1 << sint;
721        true
722    }
723}