Zide Chen | 1d15851 | 2019-09-13 14:21:05 -0700 | [diff] [blame] | 1 | // Copyright 2019 The Chromium OS Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | use crate::pci::{PciCapability, PciCapabilityID}; |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 6 | use base::{error, AsRawDescriptor, Error as SysError, Event, RawDescriptor, Tube, TubeError}; |
| 7 | |
Daniel Verkamp | 5225377 | 2021-08-18 14:20:23 -0700 | [diff] [blame] | 8 | use remain::sorted; |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 9 | use std::convert::TryInto; |
Daniel Verkamp | 5225377 | 2021-08-18 14:20:23 -0700 | [diff] [blame] | 10 | use thiserror::Error; |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 11 | use vm_control::{VmIrqRequest, VmIrqResponse}; |
Zide Chen | 1d15851 | 2019-09-13 14:21:05 -0700 | [diff] [blame] | 12 | |
| 13 | use data_model::DataInit; |
| 14 | |
| 15 | const MAX_MSIX_VECTORS_PER_DEVICE: u16 = 2048; |
Xiong Zhang | 521646a | 2019-11-08 18:36:55 +0800 | [diff] [blame] | 16 | pub const MSIX_TABLE_ENTRIES_MODULO: u64 = 16; |
| 17 | pub const MSIX_PBA_ENTRIES_MODULO: u64 = 8; |
| 18 | pub const BITS_PER_PBA_ENTRY: usize = 64; |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 19 | const FUNCTION_MASK_BIT: u16 = 0x4000; |
| 20 | const MSIX_ENABLE_BIT: u16 = 0x8000; |
| 21 | |
| 22 | #[derive(Clone)] |
| 23 | struct MsixTableEntry { |
| 24 | msg_addr_lo: u32, |
| 25 | msg_addr_hi: u32, |
| 26 | msg_data: u32, |
| 27 | vector_ctl: u32, |
| 28 | } |
| 29 | |
| 30 | impl MsixTableEntry { |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 31 | fn masked(&self) -> bool { |
| 32 | self.vector_ctl & 0x1 == 0x1 |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | impl Default for MsixTableEntry { |
| 37 | fn default() -> Self { |
| 38 | MsixTableEntry { |
| 39 | msg_addr_lo: 0, |
| 40 | msg_addr_hi: 0, |
| 41 | msg_data: 0, |
| 42 | vector_ctl: 0, |
| 43 | } |
| 44 | } |
| 45 | } |
| 46 | |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 47 | struct IrqfdGsi { |
Michael Hoyle | 685316f | 2020-09-16 15:29:20 -0700 | [diff] [blame] | 48 | irqfd: Event, |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 49 | gsi: u32, |
| 50 | } |
| 51 | |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 52 | /// Wrapper over MSI-X Capability Structure and MSI-X Tables |
| 53 | pub struct MsixConfig { |
| 54 | table_entries: Vec<MsixTableEntry>, |
| 55 | pba_entries: Vec<u64>, |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 56 | irq_vec: Vec<IrqfdGsi>, |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 57 | masked: bool, |
| 58 | enabled: bool, |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 59 | msi_device_socket: Tube, |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 60 | msix_num: u16, |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 61 | } |
| 62 | |
Daniel Verkamp | 5225377 | 2021-08-18 14:20:23 -0700 | [diff] [blame] | 63 | #[sorted] |
| 64 | #[derive(Error, Debug)] |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 65 | enum MsixError { |
Daniel Verkamp | 5225377 | 2021-08-18 14:20:23 -0700 | [diff] [blame] | 66 | #[error("AddMsiRoute failed: {0}")] |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 67 | AddMsiRoute(SysError), |
Daniel Verkamp | 5225377 | 2021-08-18 14:20:23 -0700 | [diff] [blame] | 68 | #[error("failed to receive AddMsiRoute response: {0}")] |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 69 | AddMsiRouteRecv(TubeError), |
Daniel Verkamp | 5225377 | 2021-08-18 14:20:23 -0700 | [diff] [blame] | 70 | #[error("failed to send AddMsiRoute request: {0}")] |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 71 | AddMsiRouteSend(TubeError), |
Daniel Verkamp | 5225377 | 2021-08-18 14:20:23 -0700 | [diff] [blame] | 72 | #[error("AllocateOneMsi failed: {0}")] |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 73 | AllocateOneMsi(SysError), |
Daniel Verkamp | 5225377 | 2021-08-18 14:20:23 -0700 | [diff] [blame] | 74 | #[error("failed to receive AllocateOneMsi response: {0}")] |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 75 | AllocateOneMsiRecv(TubeError), |
Daniel Verkamp | 5225377 | 2021-08-18 14:20:23 -0700 | [diff] [blame] | 76 | #[error("failed to send AllocateOneMsi request: {0}")] |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 77 | AllocateOneMsiSend(TubeError), |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 78 | } |
| 79 | |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 80 | type MsixResult<T> = std::result::Result<T, MsixError>; |
| 81 | |
Chuanxiao Dong | 45a94be | 2020-03-21 09:15:16 +0800 | [diff] [blame] | 82 | pub enum MsixStatus { |
| 83 | Changed, |
| 84 | EntryChanged(usize), |
| 85 | NothingToDo, |
| 86 | } |
| 87 | |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 88 | impl MsixConfig { |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 89 | pub fn new(msix_vectors: u16, vm_socket: Tube) -> Self { |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 90 | assert!(msix_vectors <= MAX_MSIX_VECTORS_PER_DEVICE); |
| 91 | |
| 92 | let mut table_entries: Vec<MsixTableEntry> = Vec::new(); |
| 93 | table_entries.resize_with(msix_vectors as usize, Default::default); |
| 94 | let mut pba_entries: Vec<u64> = Vec::new(); |
Peter Fang | 9d61407 | 2021-06-17 03:16:14 -0700 | [diff] [blame] | 95 | let num_pba_entries: usize = |
| 96 | ((msix_vectors as usize) + BITS_PER_PBA_ENTRY - 1) / BITS_PER_PBA_ENTRY; |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 97 | pba_entries.resize_with(num_pba_entries, Default::default); |
| 98 | |
| 99 | MsixConfig { |
| 100 | table_entries, |
| 101 | pba_entries, |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 102 | irq_vec: Vec::new(), |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 103 | masked: false, |
| 104 | enabled: false, |
Xiong Zhang | a5d248c | 2019-09-17 14:17:19 -0700 | [diff] [blame] | 105 | msi_device_socket: vm_socket, |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 106 | msix_num: msix_vectors, |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 107 | } |
| 108 | } |
| 109 | |
Daniel Verkamp | bb712d6 | 2019-11-19 09:47:33 -0800 | [diff] [blame] | 110 | /// Get the number of MSI-X vectors in this configuration. |
| 111 | pub fn num_vectors(&self) -> u16 { |
| 112 | self.msix_num |
| 113 | } |
| 114 | |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 115 | /// Check whether the Function Mask bit in Message Control word in set or not. |
| 116 | /// if 1, all of the vectors associated with the function are masked, |
| 117 | /// regardless of their per-vector Mask bit states. |
Xiong Zhang | 3185ae9 | 2019-09-05 19:29:30 +0800 | [diff] [blame] | 118 | /// If 0, each vector's Mask bit determines whether the vector is masked or not. |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 119 | pub fn masked(&self) -> bool { |
| 120 | self.masked |
| 121 | } |
| 122 | |
Chuanxiao Dong | 45a94be | 2020-03-21 09:15:16 +0800 | [diff] [blame] | 123 | /// Check whether the Function Mask bit in MSIX table Message Control |
| 124 | /// word in set or not. |
| 125 | /// If true, the vector is masked. |
| 126 | /// If false, the vector is unmasked. |
| 127 | pub fn table_masked(&self, index: usize) -> bool { |
| 128 | if index >= self.table_entries.len() { |
| 129 | true |
| 130 | } else { |
| 131 | self.table_entries[index].masked() |
| 132 | } |
| 133 | } |
| 134 | |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 135 | /// Check whether the MSI-X Enable bit in Message Control word in set or not. |
| 136 | /// if 1, the function is permitted to use MSI-X to request service. |
| 137 | pub fn enabled(&self) -> bool { |
| 138 | self.enabled |
| 139 | } |
| 140 | |
| 141 | /// Read the MSI-X Capability Structure. |
| 142 | /// The top 2 bits in Message Control word are emulated and all other |
| 143 | /// bits are read only. |
| 144 | pub fn read_msix_capability(&self, data: u32) -> u32 { |
| 145 | let mut msg_ctl = (data >> 16) as u16; |
| 146 | msg_ctl &= !(MSIX_ENABLE_BIT | FUNCTION_MASK_BIT); |
| 147 | |
| 148 | if self.enabled { |
| 149 | msg_ctl |= MSIX_ENABLE_BIT; |
| 150 | } |
| 151 | if self.masked { |
| 152 | msg_ctl |= FUNCTION_MASK_BIT; |
| 153 | } |
| 154 | (msg_ctl as u32) << 16 | (data & u16::max_value() as u32) |
| 155 | } |
| 156 | |
| 157 | /// Write to the MSI-X Capability Structure. |
| 158 | /// Only the top 2 bits in Message Control Word are writable. |
Chuanxiao Dong | 45a94be | 2020-03-21 09:15:16 +0800 | [diff] [blame] | 159 | pub fn write_msix_capability(&mut self, offset: u64, data: &[u8]) -> MsixStatus { |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 160 | if offset == 2 && data.len() == 2 { |
| 161 | let reg = u16::from_le_bytes([data[0], data[1]]); |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 162 | let old_masked = self.masked; |
| 163 | let old_enabled = self.enabled; |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 164 | |
| 165 | self.masked = (reg & FUNCTION_MASK_BIT) == FUNCTION_MASK_BIT; |
| 166 | self.enabled = (reg & MSIX_ENABLE_BIT) == MSIX_ENABLE_BIT; |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 167 | |
| 168 | if !old_enabled && self.enabled { |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 169 | if let Err(e) = self.msix_enable() { |
| 170 | error!("failed to enable MSI-X: {}", e); |
| 171 | self.enabled = false; |
| 172 | } |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | // If the Function Mask bit was set, and has just been cleared, it's |
| 176 | // important to go through the entire PBA to check if there was any |
| 177 | // pending MSI-X message to inject, given that the vector is not |
| 178 | // masked. |
| 179 | if old_masked && !self.masked { |
| 180 | for (index, entry) in self.table_entries.clone().iter().enumerate() { |
| 181 | if !entry.masked() && self.get_pba_bit(index as u16) == 1 { |
| 182 | self.inject_msix_and_clear_pba(index); |
| 183 | } |
| 184 | } |
Chuanxiao Dong | 45a94be | 2020-03-21 09:15:16 +0800 | [diff] [blame] | 185 | return MsixStatus::Changed; |
| 186 | } else if !old_masked && self.masked { |
| 187 | return MsixStatus::Changed; |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 188 | } |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 189 | } else { |
| 190 | error!( |
| 191 | "invalid write to MSI-X Capability Structure offset {:x}", |
| 192 | offset |
| 193 | ); |
| 194 | } |
Chuanxiao Dong | 45a94be | 2020-03-21 09:15:16 +0800 | [diff] [blame] | 195 | MsixStatus::NothingToDo |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 196 | } |
| 197 | |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 198 | fn add_msi_route(&self, index: u16, gsi: u32) -> MsixResult<()> { |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 199 | let mut data: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; |
| 200 | self.read_msix_table((index * 16).into(), data.as_mut()); |
| 201 | let msi_address: u64 = u64::from_le_bytes(data); |
| 202 | let mut data: [u8; 4] = [0, 0, 0, 0]; |
| 203 | self.read_msix_table((index * 16 + 8).into(), data.as_mut()); |
Xiong Zhang | a5d248c | 2019-09-17 14:17:19 -0700 | [diff] [blame] | 204 | let msi_data: u32 = u32::from_le_bytes(data); |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 205 | |
| 206 | if msi_address == 0 { |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 207 | return Ok(()); |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 208 | } |
| 209 | |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 210 | self.msi_device_socket |
| 211 | .send(&VmIrqRequest::AddMsiRoute { |
| 212 | gsi, |
| 213 | msi_address, |
| 214 | msi_data, |
| 215 | }) |
| 216 | .map_err(MsixError::AddMsiRouteSend)?; |
| 217 | if let VmIrqResponse::Err(e) = self |
| 218 | .msi_device_socket |
| 219 | .recv() |
| 220 | .map_err(MsixError::AddMsiRouteRecv)? |
| 221 | { |
| 222 | return Err(MsixError::AddMsiRoute(e)); |
Xiong Zhang | a5d248c | 2019-09-17 14:17:19 -0700 | [diff] [blame] | 223 | } |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 224 | Ok(()) |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 225 | } |
| 226 | |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 227 | fn msix_enable(&mut self) -> MsixResult<()> { |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 228 | self.irq_vec.clear(); |
| 229 | for i in 0..self.msix_num { |
Michael Hoyle | 685316f | 2020-09-16 15:29:20 -0700 | [diff] [blame] | 230 | let irqfd = Event::new().unwrap(); |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 231 | let request = VmIrqRequest::AllocateOneMsi { irqfd }; |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 232 | self.msi_device_socket |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 233 | .send(&request) |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 234 | .map_err(MsixError::AllocateOneMsiSend)?; |
Xiong Zhang | a5d248c | 2019-09-17 14:17:19 -0700 | [diff] [blame] | 235 | let irq_num: u32; |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 236 | match self |
| 237 | .msi_device_socket |
| 238 | .recv() |
| 239 | .map_err(MsixError::AllocateOneMsiRecv)? |
| 240 | { |
| 241 | VmIrqResponse::AllocateOneMsi { gsi } => irq_num = gsi, |
| 242 | VmIrqResponse::Err(e) => return Err(MsixError::AllocateOneMsi(e)), |
| 243 | _ => unreachable!(), |
Xiong Zhang | a5d248c | 2019-09-17 14:17:19 -0700 | [diff] [blame] | 244 | } |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 245 | self.irq_vec.push(IrqfdGsi { |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 246 | irqfd: match request { |
| 247 | VmIrqRequest::AllocateOneMsi { irqfd } => irqfd, |
| 248 | _ => unreachable!(), |
| 249 | }, |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 250 | gsi: irq_num, |
| 251 | }); |
| 252 | |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 253 | self.add_msi_route(i, irq_num)?; |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 254 | } |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 255 | Ok(()) |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 256 | } |
| 257 | |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 258 | /// Read MSI-X table |
| 259 | /// # Arguments |
| 260 | /// * 'offset' - the offset within the MSI-X Table |
| 261 | /// * 'data' - used to store the read results |
| 262 | /// |
| 263 | /// For all accesses to MSI-X Table and MSI-X PBA fields, software must use aligned full |
| 264 | /// DWORD or aligned full QWORD transactions; otherwise, the result is undefined. |
| 265 | /// |
Xiong Zhang | 3185ae9 | 2019-09-05 19:29:30 +0800 | [diff] [blame] | 266 | /// location: DWORD3 DWORD2 DWORD1 DWORD0 |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 267 | /// entry 0: Vector Control Msg Data Msg Upper Addr Msg Addr |
| 268 | /// entry 1: Vector Control Msg Data Msg Upper Addr Msg Addr |
| 269 | /// entry 2: Vector Control Msg Data Msg Upper Addr Msg Addr |
| 270 | /// ... |
| 271 | pub fn read_msix_table(&self, offset: u64, data: &mut [u8]) { |
| 272 | let index: usize = (offset / MSIX_TABLE_ENTRIES_MODULO) as usize; |
| 273 | let modulo_offset = offset % MSIX_TABLE_ENTRIES_MODULO; |
| 274 | |
| 275 | match data.len() { |
| 276 | 4 => { |
| 277 | let value = match modulo_offset { |
| 278 | 0x0 => self.table_entries[index].msg_addr_lo, |
| 279 | 0x4 => self.table_entries[index].msg_addr_hi, |
| 280 | 0x8 => self.table_entries[index].msg_data, |
| 281 | 0xc => self.table_entries[index].vector_ctl, |
| 282 | _ => { |
| 283 | error!("invalid offset"); |
| 284 | 0 |
| 285 | } |
| 286 | }; |
| 287 | |
| 288 | data.copy_from_slice(&value.to_le_bytes()); |
| 289 | } |
| 290 | 8 => { |
| 291 | let value = match modulo_offset { |
| 292 | 0x0 => { |
| 293 | (u64::from(self.table_entries[index].msg_addr_hi) << 32) |
| 294 | | u64::from(self.table_entries[index].msg_addr_lo) |
| 295 | } |
| 296 | 0x8 => { |
| 297 | (u64::from(self.table_entries[index].vector_ctl) << 32) |
| 298 | | u64::from(self.table_entries[index].msg_data) |
| 299 | } |
| 300 | _ => { |
| 301 | error!("invalid offset"); |
| 302 | 0 |
| 303 | } |
| 304 | }; |
| 305 | |
| 306 | data.copy_from_slice(&value.to_le_bytes()); |
| 307 | } |
| 308 | _ => error!("invalid data length"), |
| 309 | }; |
| 310 | } |
| 311 | |
| 312 | /// Write to MSI-X table |
| 313 | /// |
| 314 | /// Message Address: the contents of this field specifies the address |
| 315 | /// for the memory write transaction; different MSI-X vectors have |
| 316 | /// different Message Address values |
| 317 | /// Message Data: the contents of this field specifies the data driven |
Xiong Zhang | 3185ae9 | 2019-09-05 19:29:30 +0800 | [diff] [blame] | 318 | /// on AD[31::00] during the memory write transaction's data phase. |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 319 | /// Vector Control: only bit 0 (Mask Bit) is not reserved: when this bit |
| 320 | /// is set, the function is prohibited from sending a message using |
| 321 | /// this MSI-X Table entry. |
Chuanxiao Dong | 45a94be | 2020-03-21 09:15:16 +0800 | [diff] [blame] | 322 | pub fn write_msix_table(&mut self, offset: u64, data: &[u8]) -> MsixStatus { |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 323 | let index: usize = (offset / MSIX_TABLE_ENTRIES_MODULO) as usize; |
| 324 | let modulo_offset = offset % MSIX_TABLE_ENTRIES_MODULO; |
| 325 | |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 326 | // Store the value of the entry before modification |
| 327 | let old_entry = self.table_entries[index].clone(); |
| 328 | |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 329 | match data.len() { |
| 330 | 4 => { |
| 331 | let value = u32::from_le_bytes(data.try_into().unwrap()); |
| 332 | match modulo_offset { |
| 333 | 0x0 => self.table_entries[index].msg_addr_lo = value, |
| 334 | 0x4 => self.table_entries[index].msg_addr_hi = value, |
| 335 | 0x8 => self.table_entries[index].msg_data = value, |
| 336 | 0xc => self.table_entries[index].vector_ctl = value, |
| 337 | _ => error!("invalid offset"), |
| 338 | }; |
| 339 | } |
| 340 | 8 => { |
| 341 | let value = u64::from_le_bytes(data.try_into().unwrap()); |
| 342 | match modulo_offset { |
| 343 | 0x0 => { |
| 344 | self.table_entries[index].msg_addr_lo = (value & 0xffff_ffffu64) as u32; |
| 345 | self.table_entries[index].msg_addr_hi = (value >> 32) as u32; |
| 346 | } |
| 347 | 0x8 => { |
| 348 | self.table_entries[index].msg_data = (value & 0xffff_ffffu64) as u32; |
| 349 | self.table_entries[index].vector_ctl = (value >> 32) as u32; |
| 350 | } |
| 351 | _ => error!("invalid offset"), |
| 352 | }; |
| 353 | } |
| 354 | _ => error!("invalid data length"), |
| 355 | }; |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 356 | |
| 357 | let new_entry = self.table_entries[index].clone(); |
| 358 | if self.enabled() |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 359 | && (old_entry.msg_addr_lo != new_entry.msg_addr_lo |
| 360 | || old_entry.msg_addr_hi != new_entry.msg_addr_hi |
| 361 | || old_entry.msg_data != new_entry.msg_data) |
| 362 | { |
| 363 | let irq_num = self.irq_vec[index].gsi; |
Daniel Verkamp | 1f9c1bb | 2020-02-20 14:55:24 -0800 | [diff] [blame] | 364 | if let Err(e) = self.add_msi_route(index as u16, irq_num) { |
| 365 | error!("add_msi_route failed: {}", e); |
| 366 | } |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 367 | } |
| 368 | |
| 369 | // After the MSI-X table entry has been updated, it is necessary to |
| 370 | // check if the vector control masking bit has changed. In case the |
| 371 | // bit has been flipped from 1 to 0, we need to inject a MSI message |
| 372 | // if the corresponding pending bit from the PBA is set. Once the MSI |
| 373 | // has been injected, the pending bit in the PBA needs to be cleared. |
| 374 | // All of this is valid only if MSI-X has not been masked for the whole |
| 375 | // device. |
| 376 | |
| 377 | // Check if bit has been flipped |
Chuanxiao Dong | 45a94be | 2020-03-21 09:15:16 +0800 | [diff] [blame] | 378 | if !self.masked() { |
| 379 | if old_entry.masked() && !self.table_entries[index].masked() { |
| 380 | if self.get_pba_bit(index as u16) == 1 { |
| 381 | self.inject_msix_and_clear_pba(index); |
| 382 | } |
| 383 | return MsixStatus::EntryChanged(index); |
| 384 | } else if !old_entry.masked() && self.table_entries[index].masked() { |
| 385 | return MsixStatus::EntryChanged(index); |
| 386 | } |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 387 | } |
Chuanxiao Dong | 45a94be | 2020-03-21 09:15:16 +0800 | [diff] [blame] | 388 | MsixStatus::NothingToDo |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 389 | } |
| 390 | |
| 391 | /// Read PBA Entries |
| 392 | /// # Arguments |
| 393 | /// * 'offset' - the offset within the PBA entries |
| 394 | /// * 'data' - used to store the read results |
| 395 | /// |
| 396 | /// Pending Bits[63::00]: For each Pending Bit that is set, the function |
| 397 | /// has a pending message for the associated MSI-X Table entry. |
| 398 | pub fn read_pba_entries(&self, offset: u64, data: &mut [u8]) { |
| 399 | let index: usize = (offset / MSIX_PBA_ENTRIES_MODULO) as usize; |
| 400 | let modulo_offset = offset % MSIX_PBA_ENTRIES_MODULO; |
| 401 | |
| 402 | match data.len() { |
| 403 | 4 => { |
| 404 | let value: u32 = match modulo_offset { |
| 405 | 0x0 => (self.pba_entries[index] & 0xffff_ffffu64) as u32, |
| 406 | 0x4 => (self.pba_entries[index] >> 32) as u32, |
| 407 | _ => { |
| 408 | error!("invalid offset"); |
| 409 | 0 |
| 410 | } |
| 411 | }; |
| 412 | |
| 413 | data.copy_from_slice(&value.to_le_bytes()); |
| 414 | } |
| 415 | 8 => { |
| 416 | let value: u64 = match modulo_offset { |
| 417 | 0x0 => self.pba_entries[index], |
| 418 | _ => { |
| 419 | error!("invalid offset"); |
| 420 | 0 |
| 421 | } |
| 422 | }; |
| 423 | |
| 424 | data.copy_from_slice(&value.to_le_bytes()); |
| 425 | } |
| 426 | _ => error!("invalid data length"), |
| 427 | } |
| 428 | } |
| 429 | |
| 430 | /// Write to PBA Entries |
| 431 | /// |
| 432 | /// Software should never write, and should only read Pending Bits. |
| 433 | /// If software writes to Pending Bits, the result is undefined. |
| 434 | pub fn write_pba_entries(&mut self, _offset: u64, _data: &[u8]) { |
| 435 | error!("Pending Bit Array is read only"); |
| 436 | } |
| 437 | |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 438 | fn set_pba_bit(&mut self, vector: u16, set: bool) { |
| 439 | assert!(vector < MAX_MSIX_VECTORS_PER_DEVICE); |
| 440 | |
| 441 | let index: usize = (vector as usize) / BITS_PER_PBA_ENTRY; |
| 442 | let shift: usize = (vector as usize) % BITS_PER_PBA_ENTRY; |
| 443 | let mut mask: u64 = (1 << shift) as u64; |
| 444 | |
| 445 | if set { |
| 446 | self.pba_entries[index] |= mask; |
| 447 | } else { |
| 448 | mask = !mask; |
| 449 | self.pba_entries[index] &= mask; |
| 450 | } |
| 451 | } |
| 452 | |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 453 | fn get_pba_bit(&self, vector: u16) -> u8 { |
| 454 | assert!(vector < MAX_MSIX_VECTORS_PER_DEVICE); |
| 455 | |
| 456 | let index: usize = (vector as usize) / BITS_PER_PBA_ENTRY; |
| 457 | let shift: usize = (vector as usize) % BITS_PER_PBA_ENTRY; |
| 458 | |
| 459 | ((self.pba_entries[index] >> shift) & 0x0000_0001u64) as u8 |
| 460 | } |
Zide Chen | d6be961 | 2019-09-26 11:40:49 -0700 | [diff] [blame] | 461 | |
| 462 | fn inject_msix_and_clear_pba(&mut self, vector: usize) { |
| 463 | if let Some(irq) = self.irq_vec.get(vector) { |
| 464 | irq.irqfd.write(1).unwrap(); |
| 465 | } |
| 466 | |
| 467 | // Clear the bit from PBA |
| 468 | self.set_pba_bit(vector as u16, false); |
| 469 | } |
| 470 | |
| 471 | /// Inject virtual interrupt to the guest |
| 472 | /// |
| 473 | /// # Arguments |
| 474 | /// * 'vector' - the index to the MSI-X Table entry |
| 475 | /// |
| 476 | /// PCI Spec 3.0 6.8.3.5: while a vector is masked, the function is |
| 477 | /// prohibited from sending the associated message, and the function |
| 478 | /// must set the associated Pending bit whenever the function would |
| 479 | /// otherwise send the message. When software unmasks a vector whose |
| 480 | /// associated Pending bit is set, the function must schedule sending |
| 481 | /// the associated message, and clear the Pending bit as soon as the |
| 482 | /// message has been sent. |
| 483 | /// |
| 484 | /// If the vector is unmasked, writing to irqfd which wakes up KVM to |
| 485 | /// inject virtual interrupt to the guest. |
| 486 | pub fn trigger(&mut self, vector: u16) { |
| 487 | if self.table_entries[vector as usize].masked() || self.masked() { |
| 488 | self.set_pba_bit(vector, true); |
| 489 | } else if let Some(irq) = self.irq_vec.get(vector as usize) { |
| 490 | irq.irqfd.write(1).unwrap(); |
| 491 | } |
| 492 | } |
Xiong Zhang | a5d248c | 2019-09-17 14:17:19 -0700 | [diff] [blame] | 493 | |
| 494 | /// Return the raw fd of the MSI device socket |
Michael Hoyle | e392c46 | 2020-10-07 03:29:24 -0700 | [diff] [blame] | 495 | pub fn get_msi_socket(&self) -> RawDescriptor { |
Zach Reizner | d49bcdb | 2021-01-07 08:30:28 -0800 | [diff] [blame] | 496 | self.msi_device_socket.as_raw_descriptor() |
Xiong Zhang | a5d248c | 2019-09-17 14:17:19 -0700 | [diff] [blame] | 497 | } |
Xiong Zhang | 521646a | 2019-11-08 18:36:55 +0800 | [diff] [blame] | 498 | |
| 499 | /// Return irqfd of MSI-X Table entry |
| 500 | /// |
| 501 | /// # Arguments |
| 502 | /// * 'vector' - the index to the MSI-X table entry |
Michael Hoyle | 685316f | 2020-09-16 15:29:20 -0700 | [diff] [blame] | 503 | pub fn get_irqfd(&self, vector: usize) -> Option<&Event> { |
Xiong Zhang | 521646a | 2019-11-08 18:36:55 +0800 | [diff] [blame] | 504 | match self.irq_vec.get(vector) { |
| 505 | Some(irq) => Some(&irq.irqfd), |
| 506 | None => None, |
| 507 | } |
| 508 | } |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 509 | } |
Zide Chen | 1d15851 | 2019-09-13 14:21:05 -0700 | [diff] [blame] | 510 | |
Michael Hoyle | e392c46 | 2020-10-07 03:29:24 -0700 | [diff] [blame] | 511 | impl AsRawDescriptor for MsixConfig { |
| 512 | fn as_raw_descriptor(&self) -> RawDescriptor { |
| 513 | self.msi_device_socket.as_raw_descriptor() |
Daniel Verkamp | 10154a9 | 2020-09-28 17:44:40 -0700 | [diff] [blame] | 514 | } |
| 515 | } |
| 516 | |
Zide Chen | 1d15851 | 2019-09-13 14:21:05 -0700 | [diff] [blame] | 517 | // It is safe to implement DataInit; all members are simple numbers and any value is valid. |
| 518 | unsafe impl DataInit for MsixCap {} |
| 519 | |
| 520 | #[allow(dead_code)] |
| 521 | #[repr(C)] |
| 522 | #[derive(Clone, Copy, Default)] |
| 523 | /// MSI-X Capability Structure |
| 524 | pub struct MsixCap { |
| 525 | // To make add_capability() happy |
| 526 | _cap_vndr: u8, |
| 527 | _cap_next: u8, |
| 528 | // Message Control Register |
| 529 | // 10-0: MSI-X Table size |
| 530 | // 13-11: Reserved |
| 531 | // 14: Mask. Mask all MSI-X when set. |
| 532 | // 15: Enable. Enable all MSI-X when set. |
| 533 | msg_ctl: u16, |
| 534 | // Table. Contains the offset and the BAR indicator (BIR) |
| 535 | // 2-0: Table BAR indicator (BIR). Can be 0 to 5. |
| 536 | // 31-3: Table offset in the BAR pointed by the BIR. |
| 537 | table: u32, |
| 538 | // Pending Bit Array. Contains the offset and the BAR indicator (BIR) |
| 539 | // 2-0: PBA BAR indicator (BIR). Can be 0 to 5. |
| 540 | // 31-3: PBA offset in the BAR pointed by the BIR. |
| 541 | pba: u32, |
| 542 | } |
| 543 | |
| 544 | impl PciCapability for MsixCap { |
| 545 | fn bytes(&self) -> &[u8] { |
| 546 | self.as_slice() |
| 547 | } |
| 548 | |
| 549 | fn id(&self) -> PciCapabilityID { |
Dennis Kempin | c3dedf3 | 2021-11-12 14:42:28 -0800 | [diff] [blame^] | 550 | PciCapabilityID::Msix |
Zide Chen | 1d15851 | 2019-09-13 14:21:05 -0700 | [diff] [blame] | 551 | } |
Xiong Zhang | 12274bf | 2021-05-18 16:53:24 +0800 | [diff] [blame] | 552 | |
| 553 | fn writable_bits(&self) -> Vec<u32> { |
| 554 | // Only msg_ctl[15:14] is writable |
| 555 | vec![0x3000_0000, 0, 0] |
| 556 | } |
Zide Chen | 1d15851 | 2019-09-13 14:21:05 -0700 | [diff] [blame] | 557 | } |
| 558 | |
| 559 | impl MsixCap { |
| 560 | pub fn new( |
| 561 | table_pci_bar: u8, |
| 562 | table_size: u16, |
| 563 | table_off: u32, |
| 564 | pba_pci_bar: u8, |
| 565 | pba_off: u32, |
| 566 | ) -> Self { |
| 567 | assert!(table_size < MAX_MSIX_VECTORS_PER_DEVICE); |
| 568 | |
| 569 | // Set the table size and enable MSI-X. |
Zide Chen | 1f20497 | 2019-09-17 11:31:53 -0700 | [diff] [blame] | 570 | let msg_ctl: u16 = MSIX_ENABLE_BIT + table_size - 1; |
Zide Chen | 1d15851 | 2019-09-13 14:21:05 -0700 | [diff] [blame] | 571 | |
| 572 | MsixCap { |
| 573 | _cap_vndr: 0, |
| 574 | _cap_next: 0, |
| 575 | msg_ctl, |
| 576 | table: (table_off & 0xffff_fff8u32) | u32::from(table_pci_bar & 0x7u8), |
| 577 | pba: (pba_off & 0xffff_fff8u32) | u32::from(pba_pci_bar & 0x7u8), |
| 578 | } |
| 579 | } |
| 580 | } |