diff --git a/pcid/src/driver_interface.rs b/pcid/src/driver_interface.rs index 28c06ef02a..12a055d666 100644 --- a/pcid/src/driver_interface.rs +++ b/pcid/src/driver_interface.rs @@ -40,7 +40,9 @@ pub struct PciFunction { /// BAR sizes pub bar_sizes: [u32; 6], - /// Legacy IRQ line + /// Legacy IRQ line: It's the responsibility of pcid to make sure that it be mapped in either + /// the I/O APIC or the 8259 PIC, so that the subdriver can map the interrupt vector directly. + /// The vector to map is always this field, plus 32. pub legacy_interrupt_line: u8, /// Legacy interrupt pin (INTx#), none if INTx# interrupts aren't supported at all. @@ -114,6 +116,48 @@ pub enum PcidClientHandleError { } pub type Result = std::result::Result; +// TODO: Remove these "features" and just go strait to the actual thing. + +#[derive(Debug, Serialize, Deserialize)] +pub struct MsiSetFeatureInfo { + /// The Multi Message Enable field of the Message Control in the MSI Capability Structure, + /// is the log2 of the interrupt vectors, minus one. Can only be 0b000..=0b101. + pub multi_message_enable: Option, + + /// The system-specific message address, must be DWORD aligned. + /// + /// The message address contains things like the CPU that will be targeted, at least on + /// x86_64. + pub message_address: Option, + + /// The upper 32 bits of the 64-bit message address. Not guaranteed to exist, and is + /// reserved on x86_64 (currently). + pub message_upper_address: Option, + + /// The message data, containing the actual interrupt vector (lower 8 bits), etc. + /// + /// The spec mentions that the lower N bits can be modified, where N is the multi message + /// enable, which means that the vector set here has to be aligned to that number, and that + /// all vectors in that range have to be allocated. + pub message_data: Option, + + /// A bitmap of the vectors that are masked. This field is not guaranteed (and not likely, + /// at least according to the feature flags I got from QEMU), to exist. + pub mask_bits: Option, +} + +/// Some flags that might be set simultaneously, but separately. +#[derive(Debug, Serialize, Deserialize)] +#[non_exhaustive] +pub enum SetFeatureInfo { + Msi(MsiSetFeatureInfo), + + MsiX { + /// Masks the entire function, and all of its vectors. + function_mask: Option, + }, +} + #[derive(Debug, Serialize, Deserialize)] #[non_exhaustive] pub enum PcidClientRequest { @@ -122,12 +166,14 @@ pub enum PcidClientRequest { EnableFeature(PciFeature), FeatureStatus(PciFeature), FeatureInfo(PciFeature), + SetFeatureInfo(SetFeatureInfo), } #[derive(Debug, Serialize, Deserialize)] #[non_exhaustive] pub enum PcidServerResponseError { NonexistentFeature(PciFeature), + InvalidBitPattern, } #[derive(Debug, Serialize, Deserialize)] @@ -139,6 +185,7 @@ pub enum PcidClientResponse { FeatureStatus(PciFeature, FeatureStatus), Error(PcidServerResponseError), FeatureInfo(PciFeature, PciFeatureInfo), + SetFeatureInfo(PciFeature), } // TODO: Ideally, pcid might have its own scheme, like lots of other Redox drivers, where this kind of IPC is done. Otherwise, instead of writing serde messages over diff --git a/pcid/src/main.rs b/pcid/src/main.rs index d2369d1707..4459185018 100644 --- a/pcid/src/main.rs +++ b/pcid/src/main.rs @@ -29,7 +29,7 @@ pub struct DriverHandler { state: Arc, } -fn with_pci_func_raw T>(pci: &Pci, bus_num: u8, dev_num: u8, func_num: u8, function: F) -> T { +fn with_pci_func_raw T>(pci: &dyn CfgAccess, bus_num: u8, dev_num: u8, func_num: u8, function: F) -> T { let bus = PciBus { pci, num: bus_num, @@ -46,7 +46,7 @@ fn with_pci_func_raw T>(pci: &Pci, bus_num: u8, dev_nu } impl DriverHandler { fn with_pci_func_raw T>(&self, function: F) -> T { - with_pci_func_raw(&self.state.pci, self.bus_num, self.dev_num, self.func_num, function) + with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, function) } fn respond(&mut self, request: driver_interface::PcidClientRequest, args: &driver_interface::SubdriverArguments) -> driver_interface::PcidClientResponse { use driver_interface::*; @@ -70,7 +70,7 @@ impl DriverHandler { None => return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)), }; unsafe { - with_pci_func_raw(&self.state.pci, self.bus_num, self.dev_num, self.func_num, |func| { + with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| { capability.set_enabled(true); capability.write_message_control(func, offset); }); @@ -83,7 +83,7 @@ impl DriverHandler { None => return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)), }; unsafe { - with_pci_func_raw(&self.state.pci, self.bus_num, self.dev_num, self.func_num, |func| { + with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| { capability.set_msix_enabled(true); capability.write_a(func, offset); }); @@ -115,6 +115,56 @@ impl DriverHandler { return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(feature)); } }), + PcidClientRequest::SetFeatureInfo(info_to_set) => match info_to_set { + SetFeatureInfo::Msi(info_to_set) => if let Some((offset, info)) = self.capabilities.iter_mut().find_map(|(offset, capability)| Some((*offset, capability.as_msi_mut()?))) { + if let Some(mme) = info_to_set.multi_message_enable { + if info.multi_message_capable() < mme || mme > 0b101 { + return PcidClientResponse::Error(PcidServerResponseError::InvalidBitPattern); + } + info.set_multi_message_enable(mme); + + } + if let Some(message_addr) = info_to_set.message_address { + if message_addr & 0b11 != 0 { + return PcidClientResponse::Error(PcidServerResponseError::InvalidBitPattern); + } + info.set_message_address(message_addr); + } + if let Some(message_addr_upper) = info_to_set.message_upper_address { + info.set_message_upper_address(message_addr_upper); + } + if let Some(message_data) = info_to_set.message_data { + if message_data & ((1 << info.multi_message_enable()) - 1) != 0 { + return PcidClientResponse::Error(PcidServerResponseError::InvalidBitPattern); + } + info.set_message_data(message_data); + } + if let Some(mask_bits) = info_to_set.mask_bits { + info.set_mask_bits(mask_bits); + } + unsafe { + with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| { + info.write_all(func, offset); + }); + } + PcidClientResponse::SetFeatureInfo(PciFeature::Msi) + } else { + return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(PciFeature::Msi)); + } + SetFeatureInfo::MsiX { function_mask } => if let Some((offset, info)) = self.capabilities.iter_mut().find_map(|(offset, capability)| Some((*offset, capability.as_msix_mut()?))) { + if let Some(mask) = function_mask { + info.set_function_mask(mask); + unsafe { + with_pci_func_raw(self.state.preferred_cfg_access(), self.bus_num, self.dev_num, self.func_num, |func| { + info.write_a(func, offset); + }); + } + } + PcidClientResponse::SetFeatureInfo(PciFeature::MsiX) + } else { + return PcidClientResponse::Error(PcidServerResponseError::NonexistentFeature(PciFeature::MsiX)); + } + } } } fn handle_spawn(mut self, pcid_to_client_write: Option, pcid_from_client_read: Option, args: driver_interface::SubdriverArguments) { diff --git a/pcid/src/pci/cap.rs b/pcid/src/pci/cap.rs index d808776cec..14714cb7e9 100644 --- a/pcid/src/pci/cap.rs +++ b/pcid/src/pci/cap.rs @@ -48,13 +48,13 @@ pub enum MsiCapability { _32BitAddress { message_control: u32, message_address: u32, - message_data: u32, + message_data: u16, }, _64BitAddress { message_control: u32, message_address_lo: u32, message_address_hi: u32, - message_data: u32, + message_data: u16, }, _32BitAddressWithPvm { message_control: u32, diff --git a/pcid/src/pci/msi.rs b/pcid/src/pci/msi.rs index cd1561b755..54e2f386c6 100644 --- a/pcid/src/pci/msi.rs +++ b/pcid/src/pci/msi.rs @@ -48,13 +48,13 @@ impl MsiCapability { message_control: dword, message_address_lo: reader.read_u32(u16::from(offset + 4)), message_address_hi: reader.read_u32(u16::from(offset + 8)), - message_data: reader.read_u32(u16::from(offset + 12)), + message_data: reader.read_u32(u16::from(offset + 12)) as u16, } } else { Self::_32BitAddress { message_control: dword, message_address: reader.read_u32(u16::from(offset + 4)), - message_data: reader.read_u32(u16::from(offset + 8)), + message_data: reader.read_u32(u16::from(offset + 8)) as u16, } } } @@ -80,7 +80,7 @@ impl MsiCapability { | Self::_64BitAddressWithPvm { ref mut message_control, .. } => *message_control = new_message_control, } } - pub unsafe fn write_message_control(&mut self, writer: &W, offset: u8) { + pub unsafe fn write_message_control(&self, writer: &W, offset: u8) { writer.write_u32(u16::from(offset), self.message_control_raw()); } pub fn is_pvt_capable(&self) -> bool { @@ -100,14 +100,106 @@ impl MsiCapability { pub fn multi_message_capable(&self) -> u8 { ((self.message_control() & Self::MC_MULTI_MESSAGE_MASK) >> Self::MC_MULTI_MESSAGE_SHIFT) as u8 } - pub fn multi_message_enabled(&self) -> u8 { + pub fn multi_message_enable(&self) -> u8 { ((self.message_control() & Self::MC_MULTI_MESSAGE_ENABLE_MASK) >> Self::MC_MULTI_MESSAGE_ENABLE_SHIFT) as u8 } - pub fn set_multi_message_enabled(&mut self, log_mme: u8) { + pub fn set_multi_message_enable(&mut self, log_mme: u8) { let mut new_message_control = self.message_control() & (!Self::MC_MULTI_MESSAGE_ENABLE_MASK); new_message_control |= (u16::from(log_mme) << Self::MC_MULTI_MESSAGE_ENABLE_SHIFT); self.set_message_control(new_message_control); } + + pub fn message_address(&self) -> u32 { + match self { + &Self::_32BitAddress { message_address, .. } | &Self::_32BitAddressWithPvm { message_address, .. } => message_address, + &Self::_64BitAddress { message_address_lo, .. } | &Self::_64BitAddressWithPvm { message_address_lo, .. } => message_address_lo, + } + } + pub fn message_upper_address(&self) -> Option { + match self { + &Self::_64BitAddress { message_address_hi, .. } | &Self::_64BitAddressWithPvm { message_address_hi, .. } => Some(message_address_hi), + &Self::_32BitAddress { .. } | &Self::_32BitAddressWithPvm { .. } => None, + } + } + pub fn message_data(&self) -> u16 { + match self { + &Self::_32BitAddress { message_data, .. } | &Self::_64BitAddress { message_data, .. } => message_data, + &Self::_32BitAddressWithPvm { message_data, .. } | &Self::_64BitAddressWithPvm { message_data, .. } => message_data as u16, + } + } + pub fn mask_bits(&self) -> Option { + match self { + &Self::_32BitAddressWithPvm { mask_bits, .. } | &Self::_64BitAddressWithPvm { mask_bits, .. } => Some(mask_bits), + &Self::_32BitAddress { .. } | &Self::_64BitAddress { .. } => None, + } + } + pub fn pending_bits(&self) -> Option { + match self { + &Self::_32BitAddressWithPvm { pending_bits, .. } | &Self::_64BitAddressWithPvm { pending_bits, .. } => Some(pending_bits), + &Self::_32BitAddress { .. } | &Self::_64BitAddress { .. } => None, + } + } + pub fn set_message_address(&mut self, message_address: u32) { + assert_eq!(message_address & 0xFFFF_FFFC, message_address, "unaligned message address (this should already be validated)"); + match self { + &mut Self::_32BitAddress { message_address: ref mut addr, .. } | &mut Self::_32BitAddressWithPvm { message_address: ref mut addr, .. } => *addr = message_address, + &mut Self::_64BitAddress { message_address_lo: ref mut addr, .. } | &mut Self::_64BitAddressWithPvm { message_address_lo: ref mut addr, .. } => *addr = message_address, + } + } + pub fn set_message_upper_address(&mut self, message_upper_address: u32) -> Option<()> { + match self { + &mut Self::_64BitAddress { ref mut message_address_hi, .. } | &mut Self::_64BitAddressWithPvm { ref mut message_address_hi, .. } => *message_address_hi = message_upper_address, + &mut Self::_32BitAddress { .. } | &mut Self::_32BitAddressWithPvm { .. } => return None, + } + Some(()) + } + pub fn set_message_data(&mut self, value: u16) { + match self { + &mut Self::_32BitAddress { ref mut message_data, .. } | &mut Self::_64BitAddress { ref mut message_data, .. } => *message_data = value, + &mut Self::_32BitAddressWithPvm { ref mut message_data, .. } | &mut Self::_64BitAddressWithPvm { ref mut message_data, .. } => { + *message_data &= 0xFFFF_0000; + *message_data |= u32::from(value); + } + } + } + pub fn set_mask_bits(&mut self, mask_bits: u32) -> Option<()> { + match self { + &mut Self::_32BitAddressWithPvm { mask_bits: ref mut bits, .. } | &mut Self::_64BitAddressWithPvm { mask_bits: ref mut bits, .. } => *bits = mask_bits, + &mut Self::_32BitAddress { .. } | &mut Self::_64BitAddress { .. } => return None, + } + Some(()) + } + pub unsafe fn write_message_address(&self, writer: &W, offset: u8) { + writer.write_u32(u16::from(offset) + 4, self.message_address()) + } + pub unsafe fn write_message_upper_address(&self, writer: &W, offset: u8) -> Option<()> { + let value = self.message_upper_address()?; + writer.write_u32(u16::from(offset + 8), value); + Some(()) + } + pub unsafe fn write_message_data(&self, writer: &W, offset: u8) { + match self { + &Self::_32BitAddress { message_data, .. } => writer.write_u32(u16::from(offset + 8), message_data.into()), + &Self::_32BitAddressWithPvm { message_data, .. } => writer.write_u32(u16::from(offset + 8), message_data), + &Self::_64BitAddress { message_data, .. } => writer.write_u32(u16::from(offset + 12), message_data.into()), + &Self::_64BitAddressWithPvm { message_data, .. } => writer.write_u32(u16::from(offset + 12), message_data), + } + } + pub unsafe fn write_mask_bits(&self, writer: &W, offset: u8) -> Option<()> { + match self { + &Self::_32BitAddressWithPvm { mask_bits, .. } => writer.write_u32(u16::from(offset + 12), mask_bits), + &Self::_64BitAddressWithPvm { mask_bits, .. } => writer.write_u32(u16::from(offset + 16), mask_bits), + &Self::_32BitAddress { .. } | &Self::_64BitAddress { .. } => return None, + } + Some(()) + } + pub unsafe fn write_all(&self, writer: &W, offset: u8) { + self.write_message_control(writer, offset); + self.write_message_address(writer, offset); + self.write_message_upper_address(writer, offset); + self.write_message_data(writer, offset); + self.write_mask_bits(writer, offset); + } } impl MsixCapability { diff --git a/xhcid/src/main.rs b/xhcid/src/main.rs index 66a84b2262..d475b453c5 100644 --- a/xhcid/src/main.rs +++ b/xhcid/src/main.rs @@ -144,7 +144,7 @@ fn main() { PciFeatureInfo::MsiX(_) => panic!(), }; // use one vector - capability.set_multi_message_enabled(0); + capability.set_multi_message_enable(0); todo!("msi (msix is implemented though)") } else if msix_enabled {