1use 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#[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#[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 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
208pub struct SintProxied;
211
212impl GlobalSynic {
213 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 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 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 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 pub fn siefp(&self) -> u64 {
289 self.sints.siefp.into()
290 }
291
292 pub fn simp(&self) -> u64 {
294 self.sints.simp.into()
295 }
296
297 pub fn scontrol(&self) -> u64 {
299 self.sints.scontrol.into()
300 }
301
302 pub fn sversion(&self) -> u64 {
304 1
305 }
306
307 pub fn eom(&self) -> u64 {
309 0
310 }
311
312 pub fn sint(&self, n: u8) -> u64 {
314 self.sints.sint[n as usize].into()
315 }
316
317 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 pub fn stimer_config(&self, n: usize) -> u64 {
328 self.timers[n].config.into()
329 }
330
331 pub fn stimer_count(&self, n: usize) -> u64 {
333 self.timers[n].count
334 }
335
336 pub fn vina(&self) -> HvRegisterVsmVina {
338 self.vina
339 }
340
341 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 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 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 pub fn set_eom(&mut self, _v: u64) {
394 }
396
397 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 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 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 pub fn request_sint_readiness(&mut self, sints: u16) {
426 self.sints.pending_sints = sints;
427 }
428
429 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 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 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 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 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 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 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 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 #[must_use]
643 pub fn scan(
644 &mut self,
645 ref_time_now: u64,
646 interrupt: &mut dyn RequestInterrupt,
647 ) -> (u16, Option<u64>) {
648 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 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 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}