7dfb749b3d
Absorb redundant kernel patches into v2 carriers, remove debug and suspend patches no longer needed. Wire v2 patches in recipe.toml.
223 lines
6.7 KiB
Diff
223 lines
6.7 KiB
Diff
diff --git a/src/arch/x86_shared/device/msi.rs b/src/arch/x86_shared/device/msi.rs
|
|
--- a/src/arch/x86_shared/device/msi.rs
|
|
+++ b/src/arch/x86_shared/device/msi.rs
|
|
@@ -1,66 +1,183 @@
|
|
+// MSI/MSI-X support for x86 — kernel-level message composition and validation
|
|
+// Cross-referenced from Linux 7.0: arch/x86/kernel/apic/msi.c (391 lines)
|
|
+
|
|
use crate::arch::device::local_apic::ApicId;
|
|
|
|
pub const MSI_ADDRESS_BASE: u64 = 0xFEE0_0000;
|
|
+pub const MSI_ADDRESS_MASK: u64 = 0xFEEF_F000;
|
|
+const MSI_DEST_MODE_LOGICAL: u64 = 1 << 2;
|
|
+const MSI_REDIRECTION_HINT: u64 = 1 << 3;
|
|
+
|
|
+#[derive(Debug, Clone, Copy)]
|
|
+pub struct MsiAddress {
|
|
+ pub raw: u64,
|
|
+}
|
|
+
|
|
+#[derive(Debug, Clone, Copy)]
|
|
+pub struct MsiData {
|
|
+ pub raw: u32,
|
|
+}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct MsiMessage {
|
|
- pub address: u64,
|
|
- pub data: u32,
|
|
+ pub address: MsiAddress,
|
|
+ pub data: MsiData,
|
|
+}
|
|
+
|
|
+impl MsiAddress {
|
|
+ pub fn new(dest_apic_id: u8, redirection_hint: bool, dest_mode_logical: bool) -> Self {
|
|
+ let mut addr = MSI_ADDRESS_BASE;
|
|
+ addr |= u64::from(dest_apic_id) << 12;
|
|
+ if redirection_hint {
|
|
+ addr |= MSI_REDIRECTION_HINT;
|
|
+ }
|
|
+ if dest_mode_logical {
|
|
+ addr |= MSI_DEST_MODE_LOGICAL;
|
|
+ }
|
|
+ Self { raw: addr }
|
|
+ }
|
|
+
|
|
+ pub fn validate(addr: u64) -> bool {
|
|
+ (addr & MSI_ADDRESS_MASK) == MSI_ADDRESS_BASE
|
|
+ }
|
|
+
|
|
+ pub fn dest_apic_id(&self) -> u8 {
|
|
+ ((self.raw >> 12) & 0xFF) as u8
|
|
+ }
|
|
+}
|
|
+
|
|
+impl MsiData {
|
|
+ pub fn new(vector: u8, delivery_mode: u8, trigger_mode: u8) -> Self {
|
|
+ let mut data = u32::from(vector);
|
|
+ data |= u32::from(delivery_mode) << 8;
|
|
+ data |= u32::from(trigger_mode) << 15;
|
|
+ Self { raw: data }
|
|
+ }
|
|
+
|
|
+ pub fn vector(&self) -> u8 {
|
|
+ (self.raw & 0xFF) as u8
|
|
+ }
|
|
+
|
|
+ pub fn delivery_mode(&self) -> u8 {
|
|
+ ((self.raw >> 8) & 0x7) as u8
|
|
+ }
|
|
+
|
|
+ pub fn trigger_mode(&self) -> u8 {
|
|
+ ((self.raw >> 15) & 0x1) as u8
|
|
+ }
|
|
}
|
|
|
|
impl MsiMessage {
|
|
- pub fn compose(dest: ApicId, vector: u8, delivery_mode: u8) -> Self {
|
|
- let address = MSI_ADDRESS_BASE | (u64::from(dest.get()) << 12);
|
|
- let data = u32::from(vector) | (u32::from(delivery_mode) << 8);
|
|
+ pub fn compose(dest: ApicId, vector: u8, delivery_mode: u8, trigger_mode: u8) -> Self {
|
|
+ let address = MsiAddress::new(dest.get() as u8, false, false);
|
|
+ let data = MsiData::new(vector, delivery_mode, trigger_mode);
|
|
Self { address, data }
|
|
}
|
|
|
|
pub fn validate(&self) -> bool {
|
|
- (self.address & 0xFFF0_0000) == MSI_ADDRESS_BASE
|
|
- && self.data & 0xFF >= 32
|
|
- && self.data & 0xFF < 255
|
|
+ MsiAddress::validate(self.address.raw)
|
|
+ && self.data.vector() >= 32
|
|
+ && self.data.vector() < 255
|
|
}
|
|
}
|
|
|
|
-pub fn is_valid_msi_address(addr: u64) -> bool { (addr & 0xFFF0_0000) == MSI_ADDRESS_BASE }
|
|
-pub fn is_valid_msi_vector(vector: u8) -> bool { vector >= 32 && vector < 255 }
|
|
+pub fn is_valid_msi_address(addr: u64) -> bool {
|
|
+ MsiAddress::validate(addr)
|
|
+}
|
|
+
|
|
+pub fn is_valid_msi_vector(vector: u8) -> bool {
|
|
+ vector >= 32 && vector < 255
|
|
+}
|
|
|
|
#[derive(Debug)]
|
|
pub struct MsiCapability {
|
|
- pub msg_ctl: u16, pub msg_addr_lo: u32, pub msg_data: u16,
|
|
- pub is_64bit: bool, pub is_maskable: bool, pub multiple_message_capable: u8,
|
|
+ pub msg_ctl: u16,
|
|
+ pub msg_addr_lo: u32,
|
|
+ pub msg_addr_hi: u32,
|
|
+ pub msg_data: u16,
|
|
+ pub mask_bits: u32,
|
|
+ pub pending_bits: u32,
|
|
+ pub is_64bit: bool,
|
|
+ pub is_maskable: bool,
|
|
+ pub multiple_message_capable: u8,
|
|
}
|
|
|
|
impl MsiCapability {
|
|
- pub fn parse(raw: &[u32], msg_ctl: u16) -> Option<Self> {
|
|
- let msg_addr_lo = *raw.get(1)?;
|
|
- let msg_data = if msg_ctl & (1<<7) != 0 {
|
|
- (*raw.get(3)? & 0xFFFF) as u16
|
|
- } else {
|
|
- (*raw.get(2)? & 0xFFFF) as u16
|
|
- };
|
|
- Some(Self {
|
|
- msg_ctl, msg_addr_lo, msg_data,
|
|
- is_64bit: msg_ctl & (1<<7) != 0,
|
|
- is_maskable: msg_ctl & (1<<8) != 0,
|
|
- multiple_message_capable: ((msg_ctl>>1)&0x7) as u8,
|
|
- })
|
|
+ pub fn parse(raw: &[u32; 6], msg_ctl: u16) -> Self {
|
|
+ Self {
|
|
+ msg_ctl,
|
|
+ msg_addr_lo: raw[1],
|
|
+ msg_addr_hi: if msg_ctl & (1 << 7) != 0 { raw[2] } else { 0 },
|
|
+ msg_data: if msg_ctl & (1 << 7) != 0 {
|
|
+ (raw[3] & 0xFFFF) as u16
|
|
+ } else {
|
|
+ (raw[2] & 0xFFFF) as u16
|
|
+ },
|
|
+ mask_bits: if msg_ctl & (1 << 8) != 0 {
|
|
+ if msg_ctl & (1 << 7) != 0 {
|
|
+ raw[3] >> 16
|
|
+ } else {
|
|
+ raw[3]
|
|
+ }
|
|
+ } else {
|
|
+ 0
|
|
+ },
|
|
+ pending_bits: if msg_ctl & (1 << 8) != 0 { raw[4] } else { 0 },
|
|
+ is_64bit: msg_ctl & (1 << 7) != 0,
|
|
+ is_maskable: msg_ctl & (1 << 8) != 0,
|
|
+ multiple_message_capable: ((msg_ctl >> 1) & 0x7) as u8,
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+#[derive(Debug)]
|
|
pub struct MsixCapability {
|
|
- pub table_offset: u32, pub table_bar: u8,
|
|
- pub pba_offset: u32, pub pba_bar: u8, pub table_size: u16,
|
|
+ pub msg_ctl: u16,
|
|
+ pub table_offset: u32,
|
|
+ pub table_bar: u8,
|
|
+ pub pba_offset: u32,
|
|
+ pub pba_bar: u8,
|
|
+ pub table_size: u16,
|
|
}
|
|
|
|
impl MsixCapability {
|
|
- pub fn parse(raw: &[u32], msg_ctl: u16) -> Option<Self> {
|
|
- let r1 = *raw.get(1)?;
|
|
- let r2 = *raw.get(2)?;
|
|
- Some(Self {
|
|
- table_offset: r1 & !0x7, table_bar: (r1&0x7) as u8,
|
|
- pba_offset: r2 & !0x7, pba_bar: (r2&0x7) as u8,
|
|
- table_size: ((msg_ctl>>1)&0x7FF) as u16 + 1,
|
|
- })
|
|
+ pub fn parse(raw: &[u32; 3], msg_ctl: u16) -> Self {
|
|
+ Self {
|
|
+ msg_ctl,
|
|
+ table_offset: raw[1] & !0x7,
|
|
+ table_bar: (raw[1] & 0x7) as u8,
|
|
+ pba_offset: raw[2] & !0x7,
|
|
+ pba_bar: (raw[2] & 0x7) as u8,
|
|
+ table_size: ((msg_ctl >> 1) & 0x7FF) as u16 + 1,
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+#[cfg(test)]
|
|
+mod tests {
|
|
+ use super::*;
|
|
+
|
|
+ #[test]
|
|
+ fn test_compose_message() {
|
|
+ let msg = MsiMessage::compose(ApicId::new(3), 48, 0b101, 1);
|
|
+ assert!(msg.validate());
|
|
+ assert_eq!(msg.address.dest_apic_id(), 3);
|
|
+ assert_eq!(msg.data.vector(), 48);
|
|
+ assert_eq!(msg.data.delivery_mode(), 0b101);
|
|
+ assert_eq!(msg.data.trigger_mode(), 1);
|
|
+ }
|
|
+
|
|
+ #[test]
|
|
+ fn test_invalid_address() {
|
|
+ assert!(!is_valid_msi_address(0xDEAD_BEEF));
|
|
+ assert!(is_valid_msi_address(0xFEE0_0000));
|
|
+ }
|
|
+
|
|
+ #[test]
|
|
+ fn test_msi_parse() {
|
|
+ let raw = [0u32; 6];
|
|
+ let cap = MsiCapability::parse(&raw, 0);
|
|
+ assert!(!cap.is_64bit);
|
|
+ assert!(!cap.is_maskable);
|
|
}
|
|
}
|