xref: /cloud-hypervisor/pci/src/msi.rs (revision a3692144f0970f8d3f8ef3f8123d6a60ae982771)
1ee39e465SSebastien Boeuf // Copyright © 2019 Intel Corporation
2ee39e465SSebastien Boeuf //
3ee39e465SSebastien Boeuf // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
4ee39e465SSebastien Boeuf //
5ee39e465SSebastien Boeuf 
6f076819dSSebastien Boeuf use std::io;
74bb12a2dSSebastien Boeuf use std::sync::Arc;
888a9f799SRob Bradford 
988a9f799SRob Bradford use byteorder::{ByteOrder, LittleEndian};
1088a9f799SRob Bradford use serde::{Deserialize, Serialize};
11f076819dSSebastien Boeuf use thiserror::Error;
124bb12a2dSSebastien Boeuf use vm_device::interrupt::{
134bb12a2dSSebastien Boeuf     InterruptIndex, InterruptSourceConfig, InterruptSourceGroup, MsiIrqSourceConfig,
144bb12a2dSSebastien Boeuf };
1510ab87d6SRob Bradford use vm_migration::{MigratableError, Pausable, Snapshot, Snapshottable};
16ee39e465SSebastien Boeuf 
17ee39e465SSebastien Boeuf // MSI control masks
18ee39e465SSebastien Boeuf const MSI_CTL_ENABLE: u16 = 0x1;
19ee39e465SSebastien Boeuf const MSI_CTL_MULTI_MSG_ENABLE: u16 = 0x70;
20ee39e465SSebastien Boeuf const MSI_CTL_64_BITS: u16 = 0x80;
21ee39e465SSebastien Boeuf const MSI_CTL_PER_VECTOR: u16 = 0x100;
22ee39e465SSebastien Boeuf 
23ee39e465SSebastien Boeuf // MSI message offsets
24ee39e465SSebastien Boeuf const MSI_MSG_CTL_OFFSET: u64 = 0x2;
25ee39e465SSebastien Boeuf const MSI_MSG_ADDR_LO_OFFSET: u64 = 0x4;
26ee39e465SSebastien Boeuf 
27ee39e465SSebastien Boeuf // MSI message masks
28ee39e465SSebastien Boeuf const MSI_MSG_ADDR_LO_MASK: u32 = 0xffff_fffc;
29ee39e465SSebastien Boeuf 
msi_num_enabled_vectors(msg_ctl: u16) -> usize304bb12a2dSSebastien Boeuf pub fn msi_num_enabled_vectors(msg_ctl: u16) -> usize {
314bb12a2dSSebastien Boeuf     let field = (msg_ctl >> 4) & 0x7;
324bb12a2dSSebastien Boeuf 
334bb12a2dSSebastien Boeuf     if field > 5 {
344bb12a2dSSebastien Boeuf         return 0;
354bb12a2dSSebastien Boeuf     }
364bb12a2dSSebastien Boeuf 
374bb12a2dSSebastien Boeuf     1 << field
384bb12a2dSSebastien Boeuf }
394bb12a2dSSebastien Boeuf 
40f076819dSSebastien Boeuf #[derive(Error, Debug)]
411eac37bdSSebastien Boeuf pub enum Error {
42*a3692144SPhilipp Schuster     #[error("Failed enabling the interrupt route")]
4380e66657SPhilipp Schuster     EnableInterruptRoute(#[source] io::Error),
44*a3692144SPhilipp Schuster     #[error("Failed updating the interrupt route")]
4580e66657SPhilipp Schuster     UpdateInterruptRoute(#[source] io::Error),
46f076819dSSebastien Boeuf }
47f076819dSSebastien Boeuf 
481eac37bdSSebastien Boeuf pub const MSI_CONFIG_ID: &str = "msi_config";
491eac37bdSSebastien Boeuf 
5010ab87d6SRob Bradford #[derive(Clone, Copy, Default, Serialize, Deserialize)]
51ee39e465SSebastien Boeuf pub struct MsiCap {
52ee39e465SSebastien Boeuf     // Message Control Register
53ee39e465SSebastien Boeuf     //   0:     MSI enable.
54ee39e465SSebastien Boeuf     //   3-1;   Multiple message capable.
55ee39e465SSebastien Boeuf     //   6-4:   Multiple message enable.
56ee39e465SSebastien Boeuf     //   7:     64 bits address capable.
57ee39e465SSebastien Boeuf     //   8:     Per-vector masking capable.
58ee39e465SSebastien Boeuf     //   15-9:  Reserved.
59ee39e465SSebastien Boeuf     pub msg_ctl: u16,
60ee39e465SSebastien Boeuf     // Message Address (LSB)
61ee39e465SSebastien Boeuf     //   1-0:  Reserved.
62ee39e465SSebastien Boeuf     //   31-2: Message address.
63ee39e465SSebastien Boeuf     pub msg_addr_lo: u32,
64ee39e465SSebastien Boeuf     // Message Upper Address (MSB)
65ee39e465SSebastien Boeuf     //   31-0: Message address.
66ee39e465SSebastien Boeuf     pub msg_addr_hi: u32,
67ee39e465SSebastien Boeuf     // Message Data
68ee39e465SSebastien Boeuf     //   15-0: Message data.
69ee39e465SSebastien Boeuf     pub msg_data: u16,
70ee39e465SSebastien Boeuf     // Mask Bits
71ee39e465SSebastien Boeuf     //   31-0: Mask bits.
72ee39e465SSebastien Boeuf     pub mask_bits: u32,
73ee39e465SSebastien Boeuf     // Pending Bits
74ee39e465SSebastien Boeuf     //   31-0: Pending bits.
75ee39e465SSebastien Boeuf     pub pending_bits: u32,
76ee39e465SSebastien Boeuf }
77ee39e465SSebastien Boeuf 
78ee39e465SSebastien Boeuf impl MsiCap {
addr_64_bits(&self) -> bool79ee39e465SSebastien Boeuf     fn addr_64_bits(&self) -> bool {
80ee39e465SSebastien Boeuf         self.msg_ctl & MSI_CTL_64_BITS == MSI_CTL_64_BITS
81ee39e465SSebastien Boeuf     }
82ee39e465SSebastien Boeuf 
per_vector_mask(&self) -> bool83ee39e465SSebastien Boeuf     fn per_vector_mask(&self) -> bool {
84ee39e465SSebastien Boeuf         self.msg_ctl & MSI_CTL_PER_VECTOR == MSI_CTL_PER_VECTOR
85ee39e465SSebastien Boeuf     }
86ee39e465SSebastien Boeuf 
enabled(&self) -> bool87f3c38701SSebastien Boeuf     fn enabled(&self) -> bool {
88ee39e465SSebastien Boeuf         self.msg_ctl & MSI_CTL_ENABLE == MSI_CTL_ENABLE
89ee39e465SSebastien Boeuf     }
90ee39e465SSebastien Boeuf 
num_enabled_vectors(&self) -> usize91f3c38701SSebastien Boeuf     fn num_enabled_vectors(&self) -> usize {
924bb12a2dSSebastien Boeuf         msi_num_enabled_vectors(self.msg_ctl)
93ee39e465SSebastien Boeuf     }
94ee39e465SSebastien Boeuf 
vector_masked(&self, vector: usize) -> bool95f3c38701SSebastien Boeuf     fn vector_masked(&self, vector: usize) -> bool {
96ee39e465SSebastien Boeuf         if !self.per_vector_mask() {
97ee39e465SSebastien Boeuf             return false;
98ee39e465SSebastien Boeuf         }
99ee39e465SSebastien Boeuf 
100ee39e465SSebastien Boeuf         (self.mask_bits >> vector) & 0x1 == 0x1
101ee39e465SSebastien Boeuf     }
102ee39e465SSebastien Boeuf 
size(&self) -> u64103f3c38701SSebastien Boeuf     fn size(&self) -> u64 {
104ee39e465SSebastien Boeuf         let mut size: u64 = 0xa;
105ee39e465SSebastien Boeuf 
106ee39e465SSebastien Boeuf         if self.addr_64_bits() {
107ee39e465SSebastien Boeuf             size += 0x4;
108ee39e465SSebastien Boeuf         }
109ee39e465SSebastien Boeuf         if self.per_vector_mask() {
110ee39e465SSebastien Boeuf             size += 0xa;
111ee39e465SSebastien Boeuf         }
112ee39e465SSebastien Boeuf 
113ee39e465SSebastien Boeuf         size
114ee39e465SSebastien Boeuf     }
115ee39e465SSebastien Boeuf 
update(&mut self, offset: u64, data: &[u8])116f3c38701SSebastien Boeuf     fn update(&mut self, offset: u64, data: &[u8]) {
117ee39e465SSebastien Boeuf         // Calculate message data offset depending on the address being 32 or
118ee39e465SSebastien Boeuf         // 64 bits.
119ee39e465SSebastien Boeuf         // Calculate upper address offset if the address is 64 bits.
120ee39e465SSebastien Boeuf         // Calculate mask bits offset based on the address being 32 or 64 bits
121ee39e465SSebastien Boeuf         // and based on the per vector masking being enabled or not.
122ee39e465SSebastien Boeuf         let (msg_data_offset, addr_hi_offset, mask_bits_offset): (u64, Option<u64>, Option<u64>) =
123ee39e465SSebastien Boeuf             if self.addr_64_bits() {
124ee39e465SSebastien Boeuf                 let mask_bits = if self.per_vector_mask() {
125ee39e465SSebastien Boeuf                     Some(0x10)
126ee39e465SSebastien Boeuf                 } else {
127ee39e465SSebastien Boeuf                     None
128ee39e465SSebastien Boeuf                 };
129ee39e465SSebastien Boeuf                 (0xc, Some(0x8), mask_bits)
130ee39e465SSebastien Boeuf             } else {
131ee39e465SSebastien Boeuf                 let mask_bits = if self.per_vector_mask() {
132ee39e465SSebastien Boeuf                     Some(0xc)
133ee39e465SSebastien Boeuf                 } else {
134ee39e465SSebastien Boeuf                     None
135ee39e465SSebastien Boeuf                 };
136ee39e465SSebastien Boeuf                 (0x8, None, mask_bits)
137ee39e465SSebastien Boeuf             };
138ee39e465SSebastien Boeuf 
139ee39e465SSebastien Boeuf         // Update cache without overriding the read-only bits.
140ee39e465SSebastien Boeuf         match data.len() {
141ee39e465SSebastien Boeuf             2 => {
142ee39e465SSebastien Boeuf                 let value = LittleEndian::read_u16(data);
143ee39e465SSebastien Boeuf                 match offset {
144ee39e465SSebastien Boeuf                     MSI_MSG_CTL_OFFSET => {
145ee39e465SSebastien Boeuf                         self.msg_ctl = (self.msg_ctl & !(MSI_CTL_ENABLE | MSI_CTL_MULTI_MSG_ENABLE))
146ee39e465SSebastien Boeuf                             | (value & (MSI_CTL_ENABLE | MSI_CTL_MULTI_MSG_ENABLE))
147ee39e465SSebastien Boeuf                     }
148ee39e465SSebastien Boeuf                     x if x == msg_data_offset => self.msg_data = value,
149ee39e465SSebastien Boeuf                     _ => error!("invalid offset"),
150ee39e465SSebastien Boeuf                 }
151ee39e465SSebastien Boeuf             }
152ee39e465SSebastien Boeuf             4 => {
153ee39e465SSebastien Boeuf                 let value = LittleEndian::read_u32(data);
154ee39e465SSebastien Boeuf                 match offset {
1551379abb9SSebastien Boeuf                     0x0 => {
156ee39e465SSebastien Boeuf                         self.msg_ctl = (self.msg_ctl & !(MSI_CTL_ENABLE | MSI_CTL_MULTI_MSG_ENABLE))
157ee39e465SSebastien Boeuf                             | ((value >> 16) as u16 & (MSI_CTL_ENABLE | MSI_CTL_MULTI_MSG_ENABLE))
158ee39e465SSebastien Boeuf                     }
159ee39e465SSebastien Boeuf                     MSI_MSG_ADDR_LO_OFFSET => self.msg_addr_lo = value & MSI_MSG_ADDR_LO_MASK,
160ee39e465SSebastien Boeuf                     x if x == msg_data_offset => self.msg_data = value as u16,
161ee39e465SSebastien Boeuf                     x if addr_hi_offset.is_some() && x == addr_hi_offset.unwrap() => {
162ee39e465SSebastien Boeuf                         self.msg_addr_hi = value
163ee39e465SSebastien Boeuf                     }
164ee39e465SSebastien Boeuf                     x if mask_bits_offset.is_some() && x == mask_bits_offset.unwrap() => {
165ee39e465SSebastien Boeuf                         self.mask_bits = value
166ee39e465SSebastien Boeuf                     }
167ee39e465SSebastien Boeuf                     _ => error!("invalid offset"),
168ee39e465SSebastien Boeuf                 }
169ee39e465SSebastien Boeuf             }
170ee39e465SSebastien Boeuf             _ => error!("invalid data length"),
171ee39e465SSebastien Boeuf         }
172ee39e465SSebastien Boeuf     }
173ee39e465SSebastien Boeuf }
174f3c38701SSebastien Boeuf 
17510ab87d6SRob Bradford #[derive(Serialize, Deserialize)]
1761eac37bdSSebastien Boeuf pub struct MsiConfigState {
177f076819dSSebastien Boeuf     cap: MsiCap,
178f076819dSSebastien Boeuf }
179f076819dSSebastien Boeuf 
180f3c38701SSebastien Boeuf pub struct MsiConfig {
181a1b996acSSebastien Boeuf     pub cap: MsiCap,
182dcc646f5SSebastien Boeuf     interrupt_source_group: Arc<dyn InterruptSourceGroup>,
183f3c38701SSebastien Boeuf }
184f3c38701SSebastien Boeuf 
185f3c38701SSebastien Boeuf impl MsiConfig {
new( msg_ctl: u16, interrupt_source_group: Arc<dyn InterruptSourceGroup>, state: Option<MsiConfigState>, ) -> Result<Self, Error>1861eac37bdSSebastien Boeuf     pub fn new(
1871eac37bdSSebastien Boeuf         msg_ctl: u16,
1881eac37bdSSebastien Boeuf         interrupt_source_group: Arc<dyn InterruptSourceGroup>,
1891eac37bdSSebastien Boeuf         state: Option<MsiConfigState>,
1901eac37bdSSebastien Boeuf     ) -> Result<Self, Error> {
1911eac37bdSSebastien Boeuf         let cap = if let Some(state) = state {
1921eac37bdSSebastien Boeuf             if state.cap.enabled() {
1931eac37bdSSebastien Boeuf                 for idx in 0..state.cap.num_enabled_vectors() {
1941eac37bdSSebastien Boeuf                     let config = MsiIrqSourceConfig {
1951eac37bdSSebastien Boeuf                         high_addr: state.cap.msg_addr_hi,
1961eac37bdSSebastien Boeuf                         low_addr: state.cap.msg_addr_lo,
1971eac37bdSSebastien Boeuf                         data: state.cap.msg_data as u32,
1981eac37bdSSebastien Boeuf                         devid: 0,
199f3c38701SSebastien Boeuf                     };
200f3c38701SSebastien Boeuf 
2011eac37bdSSebastien Boeuf                     interrupt_source_group
2021eac37bdSSebastien Boeuf                         .update(
2031eac37bdSSebastien Boeuf                             idx as InterruptIndex,
2041eac37bdSSebastien Boeuf                             InterruptSourceConfig::MsiIrq(config),
2051eac37bdSSebastien Boeuf                             state.cap.vector_masked(idx),
2060149e650SYong He                             false,
2071eac37bdSSebastien Boeuf                         )
2081eac37bdSSebastien Boeuf                         .map_err(Error::UpdateInterruptRoute)?;
2091eac37bdSSebastien Boeuf                 }
2101eac37bdSSebastien Boeuf 
2111eac37bdSSebastien Boeuf                 interrupt_source_group
2120149e650SYong He                     .set_gsi()
2130149e650SYong He                     .map_err(Error::EnableInterruptRoute)?;
2140149e650SYong He 
2150149e650SYong He                 interrupt_source_group
2161eac37bdSSebastien Boeuf                     .enable()
2171eac37bdSSebastien Boeuf                     .map_err(Error::EnableInterruptRoute)?;
2181eac37bdSSebastien Boeuf             }
2191eac37bdSSebastien Boeuf 
2201eac37bdSSebastien Boeuf             state.cap
2211eac37bdSSebastien Boeuf         } else {
2221eac37bdSSebastien Boeuf             MsiCap {
2231eac37bdSSebastien Boeuf                 msg_ctl,
2241eac37bdSSebastien Boeuf                 ..Default::default()
2251eac37bdSSebastien Boeuf             }
2261eac37bdSSebastien Boeuf         };
2271eac37bdSSebastien Boeuf 
2281eac37bdSSebastien Boeuf         Ok(MsiConfig {
229f3c38701SSebastien Boeuf             cap,
2304bb12a2dSSebastien Boeuf             interrupt_source_group,
2311eac37bdSSebastien Boeuf         })
232f3c38701SSebastien Boeuf     }
233f3c38701SSebastien Boeuf 
state(&self) -> MsiConfigState234f076819dSSebastien Boeuf     fn state(&self) -> MsiConfigState {
235f076819dSSebastien Boeuf         MsiConfigState { cap: self.cap }
236f076819dSSebastien Boeuf     }
237f076819dSSebastien Boeuf 
enabled(&self) -> bool238f3c38701SSebastien Boeuf     pub fn enabled(&self) -> bool {
239f3c38701SSebastien Boeuf         self.cap.enabled()
240f3c38701SSebastien Boeuf     }
241f3c38701SSebastien Boeuf 
size(&self) -> u64242f3c38701SSebastien Boeuf     pub fn size(&self) -> u64 {
243f3c38701SSebastien Boeuf         self.cap.size()
244f3c38701SSebastien Boeuf     }
245f3c38701SSebastien Boeuf 
num_enabled_vectors(&self) -> usize2464bb12a2dSSebastien Boeuf     pub fn num_enabled_vectors(&self) -> usize {
2474bb12a2dSSebastien Boeuf         self.cap.num_enabled_vectors()
2484bb12a2dSSebastien Boeuf     }
2494bb12a2dSSebastien Boeuf 
update(&mut self, offset: u64, data: &[u8])2501a4b5eccSSebastien Boeuf     pub fn update(&mut self, offset: u64, data: &[u8]) {
2511a4b5eccSSebastien Boeuf         let old_enabled = self.cap.enabled();
2521a4b5eccSSebastien Boeuf 
2531a4b5eccSSebastien Boeuf         self.cap.update(offset, data);
2541a4b5eccSSebastien Boeuf 
2551a4b5eccSSebastien Boeuf         if self.cap.enabled() {
2564bb12a2dSSebastien Boeuf             for idx in 0..self.num_enabled_vectors() {
2574bb12a2dSSebastien Boeuf                 let config = MsiIrqSourceConfig {
2584bb12a2dSSebastien Boeuf                     high_addr: self.cap.msg_addr_hi,
2594bb12a2dSSebastien Boeuf                     low_addr: self.cap.msg_addr_lo,
2604bb12a2dSSebastien Boeuf                     data: self.cap.msg_data as u32,
261cce62375SMichael Zhao                     devid: 0,
2624bb12a2dSSebastien Boeuf                 };
2634bb12a2dSSebastien Boeuf 
264ed87e42eSRob Bradford                 if let Err(e) = self.interrupt_source_group.update(
265ed87e42eSRob Bradford                     idx as InterruptIndex,
266ed87e42eSRob Bradford                     InterruptSourceConfig::MsiIrq(config),
267ed87e42eSRob Bradford                     self.cap.vector_masked(idx),
2680149e650SYong He                     true,
269ed87e42eSRob Bradford                 ) {
2704bb12a2dSSebastien Boeuf                     error!("Failed updating vector: {:?}", e);
2714bb12a2dSSebastien Boeuf                 }
2724bb12a2dSSebastien Boeuf             }
2734bb12a2dSSebastien Boeuf 
2741a4b5eccSSebastien Boeuf             if !old_enabled {
2754bb12a2dSSebastien Boeuf                 if let Err(e) = self.interrupt_source_group.enable() {
2761a4b5eccSSebastien Boeuf                     error!("Failed enabling irq_fd: {:?}", e);
2771a4b5eccSSebastien Boeuf                 }
2781a4b5eccSSebastien Boeuf             }
2794bb12a2dSSebastien Boeuf         } else if old_enabled {
2804bb12a2dSSebastien Boeuf             if let Err(e) = self.interrupt_source_group.disable() {
2811a4b5eccSSebastien Boeuf                 error!("Failed disabling irq_fd: {:?}", e);
2821a4b5eccSSebastien Boeuf             }
2831a4b5eccSSebastien Boeuf         }
2841a4b5eccSSebastien Boeuf     }
285f3c38701SSebastien Boeuf }
286f076819dSSebastien Boeuf 
287f076819dSSebastien Boeuf impl Pausable for MsiConfig {}
288f076819dSSebastien Boeuf 
289f076819dSSebastien Boeuf impl Snapshottable for MsiConfig {
id(&self) -> String290f076819dSSebastien Boeuf     fn id(&self) -> String {
2911eac37bdSSebastien Boeuf         String::from(MSI_CONFIG_ID)
292f076819dSSebastien Boeuf     }
293f076819dSSebastien Boeuf 
snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError>294f076819dSSebastien Boeuf     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
29510ab87d6SRob Bradford         Snapshot::new_from_state(&self.state())
296f076819dSSebastien Boeuf     }
297f076819dSSebastien Boeuf }
298