Files
RedBear-OS/local/patches/base/redox.patch
T
2026-04-16 12:43:10 +01:00

1893 lines
68 KiB
Diff

diff --git a/drivers/acpid/src/acpi.rs b/drivers/acpid/src/acpi.rs
index 94a1eb17..3fd91156 100644
--- a/drivers/acpid/src/acpi.rs
+++ b/drivers/acpid/src/acpi.rs
@@ -25,6 +25,8 @@ use amlserde::{AmlSerde, AmlSerdeValue};
#[cfg(target_arch = "x86_64")]
pub mod dmar;
+#[cfg(target_arch = "x86_64")]
+use self::dmar::Dmar;
use crate::aml_physmem::{AmlPageCache, AmlPhysMemHandler};
/// The raw SDT header struct, as defined by the ACPI specification.
@@ -379,6 +381,12 @@ pub struct AcpiContext {
tables: Vec<Sdt>,
dsdt: Option<Dsdt>,
fadt: Option<Fadt>,
+ pm1a_cnt_blk: u64,
+ pm1b_cnt_blk: u64,
+ slp_typa_s5: u8,
+ slp_typb_s5: u8,
+ reset_reg: Option<GenericAddress>,
+ reset_value: u8,
aml_symbols: RwLock<AmlSymbols>,
@@ -424,6 +432,63 @@ impl AcpiContext {
.flatten()
}
+ pub fn evaluate_acpi_method(
+ &mut self,
+ path: &str,
+ method: &str,
+ args: &[u64],
+ ) -> Result<Vec<u64>, AmlEvalError> {
+ let full_path = format!("{path}.{method}");
+ let aml_name =
+ AmlName::from_str(&full_path).map_err(|_| AmlEvalError::DeserializationError)?;
+ let args = args
+ .iter()
+ .copied()
+ .map(AmlSerdeValue::Integer)
+ .collect::<Vec<_>>();
+
+ match self.aml_eval(aml_name, args)? {
+ AmlSerdeValue::Integer(value) => Ok(vec![value]),
+ AmlSerdeValue::Package { contents } => contents
+ .into_iter()
+ .map(|value| match value {
+ AmlSerdeValue::Integer(value) => Ok(value),
+ _ => Err(AmlEvalError::DeserializationError),
+ })
+ .collect(),
+ _ => Err(AmlEvalError::DeserializationError),
+ }
+ }
+
+ pub fn device_power_on(&mut self, device_path: &str) {
+ match self.evaluate_acpi_method(device_path, "_PS0", &[]) {
+ Ok(values) => {
+ log::debug!("{}._PS0 => {:?}", device_path, values);
+ }
@@
while let Some((descriptor, len)) = AnyDescriptor::parse(&data[i..]) {
descriptors.push(descriptor);
i += len;
}
+ let descriptor_count = descriptors.len();
+
+ if i < data.len() {
+ log::warn!(
+ "port {} slot {} config {} descriptor parsing stopped early at byte {} of {}",
+ port_id,
+ slot,
+ index,
+ i,
+ data.len()
+ );
+ }
+ Err(error) => {
+ log::warn!("Failed to power on {} with _PS0: {:?}", device_path, error);
+ }
+ }
+ }
+
+ pub fn device_power_off(&mut self, device_path: &str) {
+ match self.evaluate_acpi_method(device_path, "_PS3", &[]) {
+ Ok(values) => {
+ log::debug!("{}._PS3 => {:?}", device_path, values);
+ }
+ Err(error) => {
+ log::warn!("Failed to power off {} with _PS3: {:?}", device_path, error);
+ }
+ }
+ }
+
+ pub fn device_get_performance(&mut self, device_path: &str) -> Result<u64, AmlEvalError> {
+ self.evaluate_acpi_method(device_path, "_PPC", &[])?
+ .into_iter()
+ .next()
+ .ok_or(AmlEvalError::DeserializationError)
+ }
+
pub fn init(
rxsdt_physaddrs: impl Iterator<Item = u64>,
ec: Vec<(RegionSpace, Box<dyn RegionHandler>)>,
@@ -444,6 +509,12 @@ impl AcpiContext {
tables,
dsdt: None,
fadt: None,
+ pm1a_cnt_blk: 0,
+ pm1b_cnt_blk: 0,
+ slp_typa_s5: 0,
+ slp_typb_s5: 0,
+ reset_reg: None,
+ reset_value: 0,
// Temporary values
aml_symbols: RwLock::new(AmlSymbols::new(ec)),
@@ -458,7 +529,10 @@ impl AcpiContext {
}
Fadt::init(&mut this);
- //TODO (hangs on real hardware): Dmar::init(&this);
+ // DMAR (Intel VT-d) init — previously disabled due to iterator bug (type_bytes copied
+ // instead of len_bytes in DmarRawIter). Safe to call now: on AMD systems, no DMAR table
+ // exists and this returns early with a warning.
+ Dmar::init(&this);
this
}
@@ -562,92 +636,83 @@ impl AcpiContext {
aml_symbols.symbol_cache = FxHashMap::default();
}
- /// Set Power State
- /// See https://uefi.org/sites/default/files/resources/ACPI_6_1.pdf
- /// - search for PM1a
- /// See https://forum.osdev.org/viewtopic.php?t=16990 for practical details
- pub fn set_global_s_state(&self, state: u8) {
- if state != 5 {
- return;
- }
- let fadt = match self.fadt() {
- Some(fadt) => fadt,
- None => {
- log::error!("Cannot set global S-state due to missing FADT.");
- return;
- }
- };
-
- let port = fadt.pm1a_control_block as u16;
- let mut val = 1 << 13;
-
- let aml_symbols = self.aml_symbols.read();
+ pub fn acpi_shutdown(&self) {
+ let pm1a_value = (u16::from(self.slp_typa_s5) << 10) | 0x2000;
+ let pm1b_value = (u16::from(self.slp_typb_s5) << 10) | 0x2000;
- let s5_aml_name = match acpi::aml::namespace::AmlName::from_str("\\_S5") {
- Ok(aml_name) => aml_name,
- Err(error) => {
- log::error!("Could not build AmlName for \\_S5, {:?}", error);
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ {
+ let Ok(pm1a_port) = u16::try_from(self.pm1a_cnt_blk) else {
+ log::error!("PM1a_CNT_BLK address is invalid: {:#X}", self.pm1a_cnt_blk);
return;
- }
- };
+ };
- let s5 = match &aml_symbols.aml_context {
- Some(aml_context) => match aml_context.namespace.lock().get(s5_aml_name) {
- Ok(s5) => s5,
- Err(error) => {
- log::error!("Cannot set S-state, missing \\_S5, {:?}", error);
- return;
+ log::warn!(
+ "Shutdown with ACPI PM1a_CNT outw(0x{:X}, 0x{:X})",
+ pm1a_port,
+ pm1a_value
+ );
+ Pio::<u16>::new(pm1a_port).write(pm1a_value);
+
+ if self.pm1b_cnt_blk != 0 {
+ match u16::try_from(self.pm1b_cnt_blk) {
+ Ok(pm1b_port) => {
+ log::warn!(
+ "Shutdown with ACPI PM1b_CNT outw(0x{:X}, 0x{:X})",
+ pm1b_port,
+ pm1b_value
+ );
+ Pio::<u16>::new(pm1b_port).write(pm1b_value);
+ }
+ Err(_) => {
+ log::error!("PM1b_CNT_BLK address is invalid: {:#X}", self.pm1b_cnt_blk);
+ }
}
- },
- None => {
- log::error!("Cannot set S-state, AML context not initialized");
- return;
}
- };
+ }
- let package = match s5.deref() {
- acpi::aml::object::Object::Package(package) => package,
- _ => {
- log::error!("Cannot set S-state, \\_S5 is not a package");
- return;
- }
- };
+ #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+ {
+ log::error!(
+ "Cannot shutdown with ACPI PM1_CNT writes on this architecture (PM1a={:#X}, PM1b={:#X})",
+ self.pm1a_cnt_blk,
+ self.pm1b_cnt_blk
+ );
+ }
+ }
- let slp_typa = match package[0].deref() {
- acpi::aml::object::Object::Integer(i) => i.to_owned(),
- _ => {
- log::error!("typa is not an Integer");
- return;
+ pub fn acpi_reboot(&self) {
+ match self.reset_reg {
+ Some(reset_reg) => {
+ log::warn!(
+ "Reboot with ACPI reset register {:?} value {:#X}",
+ reset_reg,
+ self.reset_value
+ );
+ reset_reg.write_u8(self.reset_value);
}
- };
- let slp_typb = match package[1].deref() {
- acpi::aml::object::Object::Integer(i) => i.to_owned(),
- _ => {
- log::error!("typb is not an Integer");
- return;
+ None => {
+ log::error!("Cannot reboot with ACPI: no reset register present in FADT");
}
- };
-
- log::trace!("Shutdown SLP_TYPa {:X}, SLP_TYPb {:X}", slp_typa, slp_typb);
- val |= slp_typa as u16;
-
- #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
- {
- log::warn!("Shutdown with ACPI outw(0x{:X}, 0x{:X})", port, val);
- Pio::<u16>::new(port).write(val);
}
+ }
- // TODO: Handle SLP_TYPb
+ /// Set Power State
+ /// See https://uefi.org/sites/default/files/resources/ACPI_6_1.pdf
+ /// - search for PM1a
+ /// See https://forum.osdev.org/viewtopic.php?t=16990 for practical details
+ pub fn set_global_s_state(&self, state: u8) {
+ if state != 5 {
+ return;
+ }
- #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
- {
- log::error!(
- "Cannot shutdown with ACPI outw(0x{:X}, 0x{:X}) on this architecture",
- port,
- val
- );
+ if self.fadt().is_none() {
+ log::error!("Cannot set global S-state due to missing FADT.");
+ return;
}
+ self.acpi_shutdown();
+
loop {
core::hint::spin_loop();
}
@@ -707,7 +772,7 @@ unsafe impl plain::Plain for FadtStruct {}
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default)]
-pub struct GenericAddressStructure {
+pub struct GenericAddress {
address_space: u8,
bit_width: u8,
bit_offset: u8,
@@ -715,11 +780,77 @@ pub struct GenericAddressStructure {
address: u64,
}
diff --git a/netstack/src/scheme/icmp.rs b/netstack/src/scheme/icmp.rs
index 6365e2a7..b391bae1 100644
--- a/netstack/src/scheme/icmp.rs
+++ b/netstack/src/scheme/icmp.rs
@@ -3,7 +3,7 @@ use smoltcp::socket::icmp::{
Endpoint as IcmpEndpoint, PacketBuffer as IcmpSocketBuffer,
PacketMetadata as IcmpPacketMetadata, Socket as IcmpSocket,
};
-use smoltcp::wire::{Icmpv4Packet, Icmpv4Repr, IpAddress, IpListenEndpoint};
+use smoltcp::wire::{Icmpv4Packet, Icmpv4Repr, IpAddress, IpListenEndpoint, IpProtocol, UdpPacket};
use std::mem;
use std::str;
use syscall;
@@ -16,6 +16,10 @@ use crate::router::Router;
pub type IcmpScheme = SchemeWrapper<IcmpSocket<'static>>;
+const ICMP_UDP_TRACE_EVENT_LEN: usize = 12;
+const ICMP_UDP_TRACE_KIND_TIME_EXCEEDED: u8 = 1;
+const ICMP_UDP_TRACE_KIND_DST_UNREACHABLE: u8 = 2;
+
enum IcmpSocketType {
Echo,
Udp,
@@ -27,6 +31,79 @@ pub struct IcmpData {
ident: u16,
}
+fn encode_udp_trace_event(
+ kind: u8,
+ code: u8,
+ responder: std::net::Ipv4Addr,
+ source_port: u16,
+ dest_port: u16,
+ buf: &mut [u8],
+) -> SyscallResult<usize> {
+ if buf.len() < ICMP_UDP_TRACE_EVENT_LEN {
+ return Err(SyscallError::new(syscall::EINVAL));
+ }
+
+ buf[0] = kind;
+ buf[1] = code;
+ buf[2..4].fill(0);
+ buf[4..8].copy_from_slice(&responder.octets());
+ buf[8..10].copy_from_slice(&source_port.to_be_bytes());
+ buf[10..12].copy_from_slice(&dest_port.to_be_bytes());
+ Ok(ICMP_UDP_TRACE_EVENT_LEN)
+}
+
+fn build_udp_trace_event(
+ icmp_repr: &Icmpv4Repr<'_>,
+ responder_ip: IpAddress,
+ remote_ip: IpAddress,
+ local_port: u16,
+ buf: &mut [u8],
+) -> Option<SyscallResult<usize>> {
+ let (kind, code, header, data) = match icmp_repr {
+ Icmpv4Repr::TimeExceeded {
+ reason,
+ header,
+ data,
+ } => (
+ ICMP_UDP_TRACE_KIND_TIME_EXCEEDED,
+ u8::from(*reason),
+ header,
+ *data,
+ ),
+ Icmpv4Repr::DstUnreachable {
+ reason,
+ header,
+ data,
+ } => (
+ ICMP_UDP_TRACE_KIND_DST_UNREACHABLE,
+ u8::from(*reason),
+ header,
+ *data,
+ ),
+ _ => return None,
+ };
+
+ if header.next_header != IpProtocol::Udp || remote_ip != IpAddress::Ipv4(header.dst_addr) {
+ return None;
+ }
+
+ let udp_packet = UdpPacket::new_checked(data).ok()?;
+ if udp_packet.src_port() != local_port {
+ return None;
+ }
+
+ let IpAddress::Ipv4(responder) = responder_ip;
+
+ Some(encode_udp_trace_event(
+ kind,
+ code,
+ responder,
+ udp_packet.src_port(),
+ udp_packet.dst_port(),
+ buf,
+ ))
+}
+
impl<'a> SchemeSocket for IcmpSocket<'a> {
type SchemeDataT = PortSet;
type DataT = IcmpData;
@@ -126,6 +203,21 @@ impl<'a> SchemeSocket for IcmpSocket<'a> {
let ip =
IpAddress::from_str(addr).map_err(|_| syscall::Error::new(syscall::EINVAL))?;
+ let ident = match parts.next() {
+ Some(port) if !port.is_empty() => {
+ let port = port
+ .parse::<u16>()
+ .map_err(|_| syscall::Error::new(syscall::EINVAL))?;
+ if !ident_set.claim_port(port) {
+ return Err(SyscallError::new(syscall::EADDRINUSE));
+ }
+ port
+ }
+ Some(_) | None => ident_set
+ .get_port()
+ .ok_or_else(|| SyscallError::new(syscall::EINVAL))?,
+ };
+
let socket = IcmpSocket::new(
IcmpSocketBuffer::new(
vec![IcmpPacketMetadata::EMPTY; Smolnetd::SOCKET_BUFFER_SIZE],
@@ -138,9 +230,6 @@ impl<'a> SchemeSocket for IcmpSocket<'a> {
);
let handle = socket_set.add(socket);
let icmp_socket = socket_set.get_mut::<IcmpSocket>(handle);
- let ident = ident_set
- .get_port()
- .ok_or_else(|| SyscallError::new(syscall::EINVAL))?;
icmp_socket
.bind(IcmpEndpoint::Udp(IpListenEndpoint::from(ident)))
.map_err(|_| syscall::Error::new(syscall::EINVAL))?;
@@ -213,22 +302,39 @@ impl<'a> SchemeSocket for IcmpSocket<'a> {
return Ok(0);
}
while self.can_recv(&file.data) {
- let (payload, _) = self.recv().expect("Can't recv icmp packet");
+ let (payload, responder_ip) = self.recv().expect("Can't recv icmp packet");
let icmp_packet = Icmpv4Packet::new_unchecked(&payload);
//TODO: replace default with actual caps
- let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &Default::default()).unwrap();
+ let Ok(icmp_repr) = Icmpv4Repr::parse(&icmp_packet, &Default::default()) else {
+ continue;
+ };
- if let Icmpv4Repr::EchoReply { seq_no, data, .. } = icmp_repr {
- if buf.len() < mem::size_of::<u16>() + data.len() {
- return Err(SyscallError::new(syscall::EINVAL));
- }
- buf[0..2].copy_from_slice(&seq_no.to_be_bytes());
+ match file.data.socket_type {
+ IcmpSocketType::Echo => {
+ if let Icmpv4Repr::EchoReply { seq_no, data, .. } = icmp_repr {
+ if buf.len() < mem::size_of::<u16>() + data.len() {
+ return Err(SyscallError::new(syscall::EINVAL));
+ }
+ buf[0..2].copy_from_slice(&seq_no.to_be_bytes());
- for i in 0..data.len() {
- buf[mem::size_of::<u16>() + i] = data[i];
- }
+ for i in 0..data.len() {
+ buf[mem::size_of::<u16>() + i] = data[i];
+ }
- return Ok(mem::size_of::<u16>() + data.len());
+ return Ok(mem::size_of::<u16>() + data.len());
+ }
+ }
+ IcmpSocketType::Udp => {
+ if let Some(result) = build_udp_trace_event(
+ &icmp_repr,
+ responder_ip,
+ file.data.ip,
+ file.data.ident,
+ buf,
+ ) {
+ return result;
+ }
+ }
}
}
@@ -264,7 +370,10 @@ impl<'a> SchemeSocket for IcmpSocket<'a> {
Ok(i)
}
IcmpSocketType::Udp => {
- let path = format!("/scheme/icmp/udp/{}", socket_file.data.ip);
+ let path = format!(
+ "/scheme/icmp/udp/{}/{}",
+ socket_file.data.ip, socket_file.data.ident
+ );
let path = path.as_bytes();
let mut i = 0;
@@ -316,3 +425,123 @@ impl<'a> SchemeSocket for IcmpSocket<'a> {
Ok(0)
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::{
+ build_udp_trace_event, ICMP_UDP_TRACE_KIND_DST_UNREACHABLE,
+ ICMP_UDP_TRACE_KIND_TIME_EXCEEDED,
+ };
+ use smoltcp::wire::{
+ Icmpv4DstUnreachable, Icmpv4Repr, Icmpv4TimeExceeded, IpAddress, IpProtocol, Ipv4Address,
+ Ipv4Repr,
+ };
+
+ fn udp_header(source_port: u16, dest_port: u16) -> [u8; 8] {
+ let mut header = [0_u8; 8];
+ header[0..2].copy_from_slice(&source_port.to_be_bytes());
+ header[2..4].copy_from_slice(&dest_port.to_be_bytes());
+ header[4..6].copy_from_slice(&(8_u16).to_be_bytes());
+ header
+ }
+
+ #[test]
+ fn emits_time_exceeded_trace_event_for_matching_udp_probe() {
+ let remote = Ipv4Address::new(203, 0, 113, 9);
+ let responder = Ipv4Address::new(192, 0, 2, 1);
+ let local_port = 42_000;
+ let dest_port = 33_434;
+ let udp = udp_header(local_port, dest_port);
+ let icmp_repr = Icmpv4Repr::TimeExceeded {
+ reason: Icmpv4TimeExceeded::TtlExpired,
+ header: Ipv4Repr {
+ src_addr: Ipv4Address::new(10, 0, 2, 15),
+ dst_addr: remote,
+ next_header: IpProtocol::Udp,
+ payload_len: udp.len(),
+ hop_limit: 1,
+ },
+ data: &udp,
+ };
+ let mut encoded = [0_u8; 12];
+
+ let written = build_udp_trace_event(
+ &icmp_repr,
+ IpAddress::Ipv4(responder),
+ IpAddress::Ipv4(remote),
+ local_port,
+ &mut encoded,
+ )
+ .expect("expected traceroute event")
+ .expect("expected successful encoding");
+
+ assert_eq!(written, encoded.len());
+ assert_eq!(encoded[0], ICMP_UDP_TRACE_KIND_TIME_EXCEEDED);
+ assert_eq!(encoded[1], u8::from(Icmpv4TimeExceeded::TtlExpired));
+ assert_eq!(&encoded[4..8], &responder.octets());
+ assert_eq!(u16::from_be_bytes([encoded[8], encoded[9]]), local_port);
+ assert_eq!(u16::from_be_bytes([encoded[10], encoded[11]]), dest_port);
+ }
+
+ #[test]
+ fn ignores_udp_trace_event_for_wrong_destination() {
+ let remote = Ipv4Address::new(203, 0, 113, 9);
+ let other_remote = Ipv4Address::new(203, 0, 113, 19);
+ let udp = udp_header(42_000, 33_434);
+ let icmp_repr = Icmpv4Repr::TimeExceeded {
+ reason: Icmpv4TimeExceeded::TtlExpired,
+ header: Ipv4Repr {
+ src_addr: Ipv4Address::new(10, 0, 2, 15),
+ dst_addr: remote,
+ next_header: IpProtocol::Udp,
+ payload_len: udp.len(),
+ hop_limit: 1,
+ },
+ data: &udp,
+ };
+
+ assert!(build_udp_trace_event(
+ &icmp_repr,
+ IpAddress::Ipv4(Ipv4Address::new(192, 0, 2, 1)),
+ IpAddress::Ipv4(other_remote),
+ 42_000,
+ &mut [0_u8; 12],
+ )
+ .is_none());
+ }
+
+ #[test]
+ fn emits_destination_unreachable_trace_event() {
+ let remote = Ipv4Address::new(203, 0, 113, 9);
+ let responder = Ipv4Address::new(203, 0, 113, 9);
+ let local_port = 42_000;
+ let dest_port = 33_450;
+ let udp = udp_header(local_port, dest_port);
+ let icmp_repr = Icmpv4Repr::DstUnreachable {
+ reason: Icmpv4DstUnreachable::PortUnreachable,
+ header: Ipv4Repr {
+ src_addr: Ipv4Address::new(10, 0, 2, 15),
+ dst_addr: remote,
+ next_header: IpProtocol::Udp,
+ payload_len: udp.len(),
+ hop_limit: 64,
+ },
+ data: &udp,
+ };
+ let mut encoded = [0_u8; 12];
+
+ let written = build_udp_trace_event(
+ &icmp_repr,
+ IpAddress::Ipv4(responder),
+ IpAddress::Ipv4(remote),
+ local_port,
+ &mut encoded,
+ )
+ .expect("expected traceroute event")
+ .expect("expected successful encoding");
+
+ assert_eq!(written, encoded.len());
+ assert_eq!(encoded[0], ICMP_UDP_TRACE_KIND_DST_UNREACHABLE);
+ assert_eq!(encoded[1], u8::from(Icmpv4DstUnreachable::PortUnreachable));
+ }
+}
+impl GenericAddress {
+ pub fn is_empty(&self) -> bool {
+ self.address == 0
+ }
+
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ pub fn write_u8(&self, value: u8) {
+ match self.address_space {
+ 0 => {
+ let address_val = self.address;
+ let Ok(address) = usize::try_from(address_val) else {
+ log::error!(
+ "Reset register physical address is invalid: {:#X}",
+ address_val
+ );
+ return;
+ };
+ let page = address / PAGE_SIZE * PAGE_SIZE;
+ let offset = address % PAGE_SIZE;
+ let virt = unsafe {
+ common::physmap(
+ page,
+ PAGE_SIZE,
+ common::Prot::RW,
+ common::MemoryType::default(),
+ )
+ };
+
+ match virt {
+ Ok(virt) => unsafe {
+ (virt as *mut u8).add(offset).write_volatile(value);
+ let _ = libredox::call::munmap(virt, PAGE_SIZE);
+ },
+ Err(error) => {
+ log::error!("Failed to map ACPI reset register: {}", error);
+ }
+ }
+ }
+ 1 => match u16::try_from(self.address) {
+ Ok(port) => {
+ Pio::<u8>::new(port).write(value);
+ }
+ Err(_) => {
+ let address_val = self.address;
+ log::error!("Reset register I/O port is invalid: {:#X}", address_val);
+ }
+ },
+ address_space => {
+ log::warn!(
+ "Unsupported ACPI reset register address space {} for {:?}",
+ address_space,
+ self
+ );
+ }
+ }
+ }
+
+ #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+ pub fn write_u8(&self, _value: u8) {
+ log::error!(
+ "Cannot access ACPI reset register {:?} on this architecture",
+ self
+ );
+ }
+}
+
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
pub struct FadtAcpi2Struct {
// 12 byte structure; see below for details
- pub reset_reg: GenericAddressStructure,
+ pub reset_reg: GenericAddress,
pub reset_value: u8,
reserved3: [u8; 3],
@@ -728,14 +859,14 @@ pub struct FadtAcpi2Struct {
pub x_firmware_control: u64,
pub x_dsdt: u64,
- pub x_pm1a_event_block: GenericAddressStructure,
- pub x_pm1b_event_block: GenericAddressStructure,
- pub x_pm1a_control_block: GenericAddressStructure,
- pub x_pm1b_control_block: GenericAddressStructure,
- pub x_pm2_control_block: GenericAddressStructure,
- pub x_pm_timer_block: GenericAddressStructure,
- pub x_gpe0_block: GenericAddressStructure,
- pub x_gpe1_block: GenericAddressStructure,
+ pub x_pm1a_event_block: GenericAddress,
+ pub x_pm1b_event_block: GenericAddress,
+ pub x_pm1a_control_block: GenericAddress,
+ pub x_pm1b_control_block: GenericAddress,
+ pub x_pm2_control_block: GenericAddress,
+ pub x_pm_timer_block: GenericAddress,
+ pub x_gpe0_block: GenericAddress,
+ pub x_gpe1_block: GenericAddress,
}
unsafe impl plain::Plain for FadtAcpi2Struct {}
@@ -793,9 +924,27 @@ impl Fadt {
None => usize::try_from(fadt.dsdt).expect("expected any given u32 to fit within usize"),
};
- log::debug!("FACP at {:X}", { dsdt_ptr });
+ let pm1a_evt_blk = u64::from(fadt.pm1a_event_block);
+ let pm1b_evt_blk = u64::from(fadt.pm1b_event_block);
+ let pm1a_cnt_blk = u64::from(fadt.pm1a_control_block);
+ let pm1b_cnt_blk = u64::from(fadt.pm1b_control_block);
+ let (reset_reg, reset_value) = match fadt.acpi_2_struct() {
+ Some(fadt2) if !fadt2.reset_reg.is_empty() => {
+ (Some(fadt2.reset_reg), fadt2.reset_value)
+ }
+ _ => (None, 0),
+ };
- let dsdt_sdt = match Sdt::load_from_physical(fadt.dsdt as usize) {
+ log::debug!("FACP at {:X}", { dsdt_ptr });
+ log::debug!(
+ "FADT power blocks: PM1a_EVT={:#X}, PM1b_EVT={:#X}, PM1a_CNT={:#X}, PM1b_CNT={:#X}",
+ pm1a_evt_blk,
+ pm1b_evt_blk,
+ pm1a_cnt_blk,
+ pm1b_cnt_blk
+ );
+
+ let dsdt_sdt = match Sdt::load_from_physical(dsdt_ptr) {
Ok(dsdt) => dsdt,
Err(error) => {
log::error!("Failed to load DSDT: {}", error);
@@ -803,8 +952,48 @@ impl Fadt {
}
};
+ let (slp_typa_s5, slp_typb_s5) = match AmlName::from_str("\\_S5") {
+ Ok(s5_name) => match context.aml_eval(s5_name, Vec::new()) {
+ Ok(AmlSerdeValue::Package { contents }) => match (contents.get(0), contents.get(1))
+ {
+ (
+ Some(AmlSerdeValue::Integer(slp_typa)),
+ Some(AmlSerdeValue::Integer(slp_typb)),
+ ) => match (u8::try_from(*slp_typa), u8::try_from(*slp_typb)) {
+ (Ok(slp_typa_s5), Ok(slp_typb_s5)) => (slp_typa_s5, slp_typb_s5),
+ _ => {
+ log::warn!("\\_S5 values do not fit in u8: {:?}", contents);
+ (0, 0)
+ }
+ },
+ _ => {
+ log::warn!("\\_S5 package did not contain two integers: {:?}", contents);
+ (0, 0)
+ }
+ },
+ Ok(value) => {
+ log::warn!("\\_S5 returned unexpected AML value: {:?}", value);
+ (0, 0)
+ }
+ Err(error) => {
+ log::warn!("Failed to evaluate \\_S5: {:?}", error);
+ (0, 0)
+ }
+ },
+ Err(error) => {
+ log::warn!("Could not build AmlName for \\_S5: {:?}", error);
+ (0, 0)
+ }
+ };
+
context.fadt = Some(fadt.clone());
context.dsdt = Some(Dsdt(dsdt_sdt.clone()));
+ context.pm1a_cnt_blk = pm1a_cnt_blk;
+ context.pm1b_cnt_blk = pm1b_cnt_blk;
+ context.slp_typa_s5 = slp_typa_s5;
+ context.slp_typb_s5 = slp_typb_s5;
+ context.reset_reg = reset_reg;
+ context.reset_value = reset_value;
context.tables.push(dsdt_sdt);
}
diff --git a/drivers/acpid/src/acpi/dmar/mod.rs b/drivers/acpid/src/acpi/dmar/mod.rs
index c42b379a..3024f58e 100644
--- a/drivers/acpid/src/acpi/dmar/mod.rs
+++ b/drivers/acpid/src/acpi/dmar/mod.rs
@@ -471,13 +471,17 @@ impl<'sdt> Iterator for DmarRawIter<'sdt> {
let type_bytes = <[u8; 2]>::try_from(type_bytes)
.expect("expected a 2-byte slice to be convertible to [u8; 2]");
- let len_bytes = <[u8; 2]>::try_from(type_bytes)
+ let len_bytes = <[u8; 2]>::try_from(len_bytes)
.expect("expected a 2-byte slice to be convertible to [u8; 2]");
- let ty = u16::from_ne_bytes(type_bytes);
- let len = u16::from_ne_bytes(len_bytes);
+ let len = u16::from_ne_bytes(len_bytes) as usize;
- let len = usize::try_from(len).expect("expected u16 to fit within usize");
+ // Validate minimum entry header size and prevent infinite loops
+ if len < 4 || len > self.bytes.len() {
+ return None;
+ }
+
+ let ty = u16::from_ne_bytes(type_bytes);
if len > remainder.len() {
log::warn!("DMAR remapping structure length was smaller than the remaining length of the table.");
diff --git a/drivers/acpid/src/main.rs b/drivers/acpid/src/main.rs
index 0933f638..d4b0f3d0 100644
--- a/drivers/acpid/src/main.rs
+++ b/drivers/acpid/src/main.rs
@@ -7,11 +7,14 @@ use std::sync::Arc;
use ::acpi::aml::op_region::{RegionHandler, RegionSpace};
use event::{EventFlags, RawEventQueue};
use redox_scheme::{
- scheme::{register_sync_scheme, SchemeState, SchemeSync},
+ scheme::{register_sync_scheme, Op, SchemeState, SchemeSync},
RequestKind, Response, SignalBehavior, Socket,
};
use syscall::{EAGAIN, EWOULDBLOCK};
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+use common::io::{Io, Pio};
+
mod acpi;
mod aml_physmem;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
@@ -103,6 +106,7 @@ fn daemon(daemon: daemon::Daemon) -> ! {
libredox::call::setrens(0, 0).expect("acpid: failed to enter null namespace");
let mut mounted = true;
+ let mut reboot_requested = false;
while mounted {
let Some(event) = event_queue
.next()
@@ -130,7 +134,34 @@ fn daemon(daemon: daemon::Daemon) -> ! {
};
match req.kind() {
RequestKind::Call(call) => {
- let response = call.handle_sync(&mut scheme, &mut state);
+ let caller = call.caller();
+ let op = match call.op() {
+ Ok(op) => op,
+ Err(call) => {
+ let response = Response::new(
+ Err(syscall::Error::new(syscall::ENOSYS)),
+ call,
+ );
+ socket
+ .write_response(response, SignalBehavior::Restart)
+ .expect("acpid: failed to write response");
+ continue;
+ }
+ };
+
+ if let Op::OpenAt(openat) = &op {
+ if openat.path().contains("reboot") {
+ log::info!(
+ "Received reboot request from acpi scheme path: {}",
+ openat.path()
+ );
+ reboot_requested = true;
+ mounted = false;
+ break;
+ }
+ }
+
+ let response = op.handle_sync(caller, &mut scheme, &mut state);
socket
.write_response(response, SignalBehavior::Restart)
.expect("acpid: failed to write response");
@@ -162,9 +193,19 @@ fn daemon(daemon: daemon::Daemon) -> ! {
drop(shutdown_pipe);
drop(event_queue);
- acpi_context.set_global_s_state(5);
+ if reboot_requested {
+ acpi_context.acpi_reboot();
+
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ {
+ log::warn!("Falling back to keyboard controller reset.");
+ Pio::<u8>::new(0x64).write(0xFE);
+ }
+ } else {
+ acpi_context.set_global_s_state(5);
+ }
- unreachable!("System should have shut down before this is entered");
+ unreachable!("System should have shut down or rebooted before this is entered");
}
fn main() {
diff --git a/drivers/pcid/src/scheme.rs b/drivers/pcid/src/scheme.rs
index c2caf804..95acdb57 100644
--- a/drivers/pcid/src/scheme.rs
+++ b/drivers/pcid/src/scheme.rs
@@ -21,6 +21,7 @@ enum Handle {
TopLevel { entries: Vec<String> },
Access,
Device,
+ Config { addr: PciAddress },
Channel { addr: PciAddress, st: ChannelState },
SchemeRoot,
}
@@ -30,14 +31,20 @@ struct HandleWrapper {
}
impl Handle {
fn is_file(&self) -> bool {
- matches!(self, Self::Access | Self::Channel { .. })
+ matches!(
+ self,
+ Self::Access | Self::Config { .. } | Self::Channel { .. }
+ )
}
fn is_dir(&self) -> bool {
!self.is_file()
}
// TODO: capability rather than root
fn requires_root(&self) -> bool {
- matches!(self, Self::Access | Self::Channel { .. })
+ matches!(
+ self,
+ Self::Access | Self::Config { .. } | Self::Channel { .. }
+ )
}
fn is_scheme_root(&self) -> bool {
matches!(self, Self::SchemeRoot)
@@ -132,6 +139,7 @@ impl SchemeSync for PciScheme {
let (len, mode) = match handle.inner {
Handle::TopLevel { ref entries } => (entries.len(), MODE_DIR | 0o755),
Handle::Device => (DEVICE_CONTENTS.len(), MODE_DIR | 0o755),
+ Handle::Config { .. } => (256, MODE_CHR | 0o600),
Handle::Access | Handle::Channel { .. } => (0, MODE_CHR | 0o600),
Handle::SchemeRoot => return Err(Error::new(EBADF)),
};
@@ -156,6 +164,18 @@ impl SchemeSync for PciScheme {
match handle.inner {
Handle::TopLevel { .. } => Err(Error::new(EISDIR)),
Handle::Device => Err(Error::new(EISDIR)),
+ Handle::Config { addr } => {
+ let offset = _offset as u16;
+ let dword_offset = offset & !0x3;
+ let byte_offset = (offset & 0x3) as usize;
+ let bytes_to_read = buf.len().min(4 - byte_offset);
+
+ let dword = unsafe { self.pcie.read(addr, dword_offset) };
+ let bytes = dword.to_le_bytes();
+ buf[..bytes_to_read]
+ .copy_from_slice(&bytes[byte_offset..byte_offset + bytes_to_read]);
+ Ok(bytes_to_read)
+ }
Handle::Channel {
addr: _,
ref mut st,
@@ -193,7 +213,9 @@ impl SchemeSync for PciScheme {
return Ok(buf);
}
Handle::Device => DEVICE_CONTENTS,
- Handle::Access | Handle::Channel { .. } => return Err(Error::new(ENOTDIR)),
+ Handle::Access | Handle::Config { .. } | Handle::Channel { .. } => {
+ return Err(Error::new(ENOTDIR));
+ }
Handle::SchemeRoot => return Err(Error::new(EBADF)),
};
@@ -223,6 +245,20 @@ impl SchemeSync for PciScheme {
}
match handle.inner {
+ Handle::Config { addr } => {
+ let offset = _offset as u16;
+ let dword_offset = offset & !0x3;
+ let byte_offset = (offset & 0x3) as usize;
+ let bytes_to_write = buf.len().min(4 - byte_offset);
+
+ let mut dword = unsafe { self.pcie.read(addr, dword_offset) };
+ let mut bytes = dword.to_le_bytes();
+ bytes[byte_offset..byte_offset + bytes_to_write]
+ .copy_from_slice(&buf[..bytes_to_write]);
+ dword = u32::from_le_bytes(bytes);
+ unsafe { self.pcie.write(addr, dword_offset, dword) };
+ Ok(buf.len())
+ }
Handle::Channel { addr, ref mut st } => {
Self::write_channel(&self.pcie, &mut self.tree, addr, st, buf)
}
@@ -318,6 +354,10 @@ impl PciScheme {
func.enabled = false;
}
}
+ Some(HandleWrapper {
+ inner: Handle::Config { .. },
+ ..
+ }) => {}
_ => {}
}
}
@@ -343,6 +383,7 @@ impl PciScheme {
let path = &after[1..];
match path {
+ "config" => Handle::Config { addr },
"channel" => {
if func.enabled {
return Err(Error::new(ENOLCK));
diff --git a/init/src/scheduler.rs b/init/src/scheduler.rs
index 670a5526..24ce3d68 100644
--- a/init/src/scheduler.rs
+++ b/init/src/scheduler.rs
@@ -1,8 +1,8 @@
use std::collections::VecDeque;
-use crate::InitConfig;
use crate::script::Command;
use crate::unit::{Unit, UnitId, UnitKind, UnitStore};
+use crate::InitConfig;
pub struct Scheduler {
pending: VecDeque<Job>,
@@ -96,14 +96,9 @@ fn run(unit: &mut Unit, config: &mut InitConfig) {
eprintln!("Skipping '{} {}'", service.cmd, service.args.join(" "));
return;
}
- if config.log_debug {
- eprintln!(
- "Starting {} ({})",
- unit.info.description.as_ref().unwrap_or(&unit.id.0),
- service.cmd,
- );
- }
+ eprintln!("init: starting {} ({:?})", service.cmd, &service.type_,);
service.spawn(&config.envs);
+ eprintln!("init: started {} done", service.cmd,);
}
UnitKind::Target {} => {
if config.log_debug {
diff --git a/drivers/storage/usbscsid/src/main.rs b/drivers/storage/usbscsid/src/main.rs
--- a/drivers/storage/usbscsid/src/main.rs
+++ b/drivers/storage/usbscsid/src/main.rs
@@
fn main() {
daemon::Daemon::new(daemon);
}
fn daemon(daemon: daemon::Daemon) -> ! {
- let mut args = env::args().skip(1);
+ if let Err(err) = run(daemon) {
+ eprintln!("usbscsid: startup failed: {err}");
+ std::process::exit(1);
+ }
+
+ std::process::exit(0);
+}
+
+fn run(daemon: daemon::Daemon) -> Result<(), String> {
+ let mut args = env::args().skip(1);
const USAGE: &'static str = "usbscsid <scheme> <port> <protocol>";
- let scheme = args.next().expect(USAGE);
- let port = args
- .next()
- .expect(USAGE)
- .parse::<PortId>()
- .expect("Expected port ID");
- let protocol = args
- .next()
- .expect(USAGE)
- .parse::<u8>()
- .expect("protocol has to be a number 0-255");
+ let scheme = args.next().ok_or_else(|| USAGE.to_string())?;
+ let port_arg = args.next().ok_or_else(|| USAGE.to_string())?;
+ let protocol_arg = args.next().ok_or_else(|| USAGE.to_string())?;
+ let port = port_arg
+ .parse::<PortId>()
+ .map_err(|err| format!("invalid port id `{port_arg}`: {err}"))?;
+ let protocol = protocol_arg
+ .parse::<u8>()
+ .map_err(|err| format!("invalid protocol `{protocol_arg}`: {err}"))?;
@@
- let handle =
- XhciClientHandle::new(scheme.to_owned(), port).expect("Failed to open XhciClientHandle");
+ let handle = XhciClientHandle::new(scheme.to_owned(), port)
+ .map_err(|err| format!("failed to open XHCI client handle: {err}"))?;
let desc = handle
.get_standard_descs()
- .expect("Failed to get standard descriptors");
+ .map_err(|err| format!("failed to get standard descriptors: {err}"))?;
@@
- .expect("Failed to find suitable configuration");
+ .ok_or_else(|| "failed to find suitable BOT configuration".to_string())?;
@@
- .expect("Failed to configure endpoints");
+ .map_err(|err| format!("failed to configure endpoints: {err}"))?;
- let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc)
- .expect("Failed to setup protocol");
+ let mut protocol = protocol::setup(&handle, protocol, &desc, &conf_desc, &if_desc)
+ .map_err(|err| format!("failed to setup protocol: {err}"))?;
@@
- let mut scsi = Scsi::new(&mut *protocol).expect("usbscsid: failed to setup SCSI");
+ let mut scsi =
+ Scsi::new(&mut *protocol).map_err(|err| format!("failed to setup SCSI: {err}"))?;
println!("SCSI initialized");
- let mut buffer = [0u8; 512];
- scsi.read(&mut *protocol, 0, &mut buffer).unwrap();
- println!("DISK CONTENT: {}", base64::encode(&buffer[..]));
- let event_queue = event::EventQueue::new().unwrap();
+ let event_queue =
+ event::EventQueue::new().map_err(|err| format!("failed to create event queue: {err}"))?;
@@
- .unwrap();
+ .map_err(|err| format!("failed to subscribe scheme event handle: {err}"))?;
for event in event_queue {
- match event.unwrap().user_data {
- Event::Scheme => driver_block::FuturesExecutor
- .block_on(scheme.tick())
- .unwrap(),
+ let event = match event {
+ Ok(event) => event,
+ Err(err) => {
+ eprintln!("usbscsid: event queue error: {err}");
+ continue;
+ }
+ };
+
+ match event.user_data {
+ Event::Scheme => {
+ if let Err(err) = driver_block::FuturesExecutor.block_on(scheme.tick()) {
+ eprintln!("usbscsid: scheme tick failed: {err}");
+ }
+ }
}
}
- std::process::exit(0);
+ Ok(())
}
diff --git a/drivers/storage/usbscsid/src/protocol/mod.rs b/drivers/storage/usbscsid/src/protocol/mod.rs
--- a/drivers/storage/usbscsid/src/protocol/mod.rs
+++ b/drivers/storage/usbscsid/src/protocol/mod.rs
@@
#[error("attempted recovery failed")]
RecoveryFailed,
+ #[error("unsupported USB mass-storage protocol 0x{0:02X}")]
+ UnsupportedProtocol(u8),
+
#[error("protocol error")]
ProtocolError(&'static str),
}
@@
pub fn setup<'a>(
handle: &'a XhciClientHandle,
protocol: u8,
- dev_desc: &DevDesc,
+ _dev_desc: &DevDesc,
conf_desc: &ConfDesc,
if_desc: &IfDesc,
-) -> Option<Box<dyn Protocol + 'a>> {
+) -> Result<Box<dyn Protocol + 'a>, ProtocolError> {
match protocol {
- 0x50 => Some(Box::new(
- BulkOnlyTransport::init(handle, conf_desc, if_desc).unwrap(),
- )),
- _ => None,
+ 0x50 => BulkOnlyTransport::init(handle, conf_desc, if_desc)
+ .map(|transport| Box::new(transport) as Box<dyn Protocol + 'a>),
+ _ => Err(ProtocolError::UnsupportedProtocol(protocol)),
}
}
diff --git a/drivers/storage/usbscsid/src/scsi/mod.rs b/drivers/storage/usbscsid/src/scsi/mod.rs
--- a/drivers/storage/usbscsid/src/scsi/mod.rs
+++ b/drivers/storage/usbscsid/src/scsi/mod.rs
@@
#[error("overflow")]
Overflow(&'static str),
+
+ #[error("SCSI command failed: {0}")]
+ CommandFailed(&'static str),
}
@@
if let SendCommandStatus {
kind: SendCommandStatusKind::Failed,
..
} = protocol.send_command(
&self.command_buffer[..10],
DeviceReqData::In(&mut self.data_buffer[..initial_alloc_len as usize]),
)? {
self.get_ff_sense(protocol, 252)?;
- panic!("{:?}", self.res_ff_sense_data());
+ eprintln!(
+ "usbscsid: MODE SENSE(10) failed; sense data: {:?}",
+ self.res_ff_sense_data()
+ );
+ return Err(ScsiError::CommandFailed("MODE SENSE(10)"));
}
diff --git a/drivers/usb/xhcid/src/xhci/event.rs b/drivers/usb/xhcid/src/xhci/event.rs
--- a/drivers/usb/xhcid/src/xhci/event.rs
+++ b/drivers/usb/xhcid/src/xhci/event.rs
@@
#[test]
fn grow_preserves_existing_ring_state() {
- let mut ring = EventRing::new::<super::super::CONTEXT_64>(true).unwrap();
+ let mut ring = EventRing::new::<{ super::super::CONTEXT_64 }>(true).unwrap();
@@
- ring.grow::<super::super::CONTEXT_64>(true, old_len + 64)
+ ring.grow::<{ super::super::CONTEXT_64 }>(true, old_len + 64)
.unwrap();
diff --git a/drivers/usb/xhcid/src/xhci/mod.rs b/drivers/usb/xhcid/src/xhci/mod.rs
--- a/drivers/usb/xhcid/src/xhci/mod.rs
+++ b/drivers/usb/xhcid/src/xhci/mod.rs
@@
let dev_desc = self.get_desc(port_id, slot).await?;
debug!("Got the full device descriptor!");
+ info!(
+ "port {} slot {} device {:04X}:{:04X} class {}.{} proto {} has {} configuration(s)",
+ port_id,
+ slot,
+ dev_desc.vendor,
+ dev_desc.product,
+ dev_desc.class,
+ dev_desc.sub_class,
+ dev_desc.protocol,
+ dev_desc.config_descs.len()
+ );
self.port_states.get_mut(&port_id).unwrap().dev_desc = Some(dev_desc);
@@
debug!("Updated the default control pipe");
+ info!("port {} slot {} starting subdriver matching", port_id, slot);
match self.spawn_drivers(port_id) {
Ok(()) => (),
@@
trace!("Got config and device descriptors on port {}", port);
let drivers_usercfg: &DriversConfig = &DRIVERS_CONFIG;
+
+ if config_desc.interface_descs.is_empty() {
+ warn!(
+ "No interface descriptors found for port {} in configuration {}; no subdrivers can be matched",
+ port,
+ config_desc.configuration_value
+ );
+ }
for ifdesc in config_desc.interface_descs.iter() {
diff --git a/drivers/usb/xhcid/src/xhci/scheme.rs b/drivers/usb/xhcid/src/xhci/scheme.rs
--- a/drivers/usb/xhcid/src/xhci/scheme.rs
+++ b/drivers/usb/xhcid/src/xhci/scheme.rs
@@
pub enum AnyDescriptor {
// These are the ones that I have found, but there are more.
Device(usb::DeviceDescriptor),
Config(usb::ConfigDescriptor),
Interface(usb::InterfaceDescriptor),
Endpoint(usb::EndpointDescriptor),
Hid(usb::HidDescriptor),
SuperSpeedCompanion(usb::SuperSpeedCompanionDescriptor),
SuperSpeedPlusCompanion(usb::SuperSpeedPlusIsochCmpDescriptor),
+ Unknown { kind: u8 },
}
impl AnyDescriptor {
@@
- _ => {
- //panic!("Descriptor unknown {}: bytes {:#0x?}", kind, bytes);
- return None;
- }
+ _ => Self::Unknown { kind },
},
len.into(),
))
@@
- let mut interface_descs = SmallVec::new();
+ let mut interface_descs = SmallVec::<[IfDesc; 1]>::new();
let mut iter = descriptors.into_iter().peekable();
@@
Some(AnyDescriptor::Hid(h)) if idesc.class == 3 => {
hid_descs.push(h.into());
continue;
}
+ Some(AnyDescriptor::Unknown { kind }) => {
+ log::warn!(
+ "port {} slot {} iface {} skipping unknown descriptor kind {} while collecting endpoints",
+ port_id,
+ slot,
+ idesc.number,
+ kind
+ );
+ continue;
+ }
Some(unexpected) => {
log::warn!("expected endpoint, got {:X?}", unexpected);
break;
@@
} else {
log::warn!("expected interface, got {:?}", item);
// TODO
//break;
}
}
+
+ log::info!(
+ "port {} slot {} config {} parsed {} descriptor entries into {} interface(s)",
+ port_id,
+ slot,
+ index,
+ descriptor_count,
+ interface_descs.len()
+ );
+
+ for if_desc in interface_descs.iter() {
+ let number: u8 = if_desc.number;
+ let alternate_setting: u8 = if_desc.alternate_setting;
+ let class: u8 = if_desc.class;
+ let sub_class: u8 = if_desc.sub_class;
+ let protocol: u8 = if_desc.protocol;
+ let endpoint_count: usize = if_desc.endpoints.len();
+ log::info!(
+ "port {} slot {} config {} iface {} alt {} class {}.{} proto {} endpoints {}",
+ port_id,
+ slot,
+ index,
+ number,
+ alternate_setting,
+ class,
+ sub_class,
+ protocol,
+ endpoint_count
+ );
+ }
diff --git a/drivers/audio/ac97d/src/main.rs b/drivers/audio/ac97d/src/main.rs
--- a/drivers/audio/ac97d/src/main.rs
+++ b/drivers/audio/ac97d/src/main.rs
@@
fn daemon(daemon: daemon::Daemon, pcid_handle: PciFunctionHandle) -> ! {
let pci_config = pcid_handle.config();
let mut name = pci_config.func.name();
name.push_str("_ac97");
+
+ common::setup_logging(
+ "audio",
+ "pci",
+ &name,
+ common::output_level(),
+ common::file_level(),
+ );
+
+ let bar0 = match pci_config.func.bars[0] {
+ pcid_interface::PciBar::Port(port) => port,
+ ref other => {
+ log::warn!(
+ "ac97d: unsupported BAR0 layout for {}: expected port BAR, found {}",
+ pci_config.func.display(),
+ other.display(),
+ );
+ std::process::exit(0);
+ }
+ };
+ let bar1 = match pci_config.func.bars[1] {
+ pcid_interface::PciBar::Port(port) => port,
+ ref other => {
+ log::warn!(
+ "ac97d: unsupported BAR1 layout for {}: expected port BAR, found {}",
+ pci_config.func.display(),
+ other.display(),
+ );
+ std::process::exit(0);
+ }
+ };
-
- let bar0 = pci_config.func.bars[0].expect_port();
- let bar1 = pci_config.func.bars[1].expect_port();
@@
println!(" + ac97 {}", pci_config.func.display());
-
- common::setup_logging(
- "audio",
- "pci",
- &name,
- common::output_level(),
- common::file_level(),
- );
diff --git a/drivers/input/ps2d/src/main.rs b/drivers/input/ps2d/src/main.rs
--- a/drivers/input/ps2d/src/main.rs
+++ b/drivers/input/ps2d/src/main.rs
@@
use common::acquire_port_io_rights;
use event::{user_data, EventQueue};
use inputd::ProducerHandle;
+use log::{error, warn};
@@
mod controller;
mod mouse;
mod state;
mod vm;
+
+fn exit_bootsafe(daemon: &mut Option<daemon::Daemon>, reason: &str) -> ! {
+ warn!("ps2d: {}; disabling PS/2 input for this boot", reason);
+ if let Some(daemon) = daemon.take() {
+ daemon.ready();
+ }
+ process::exit(0);
+}
@@
+ let mut daemon = Some(daemon);
+
+ if let Err(err) = acquire_port_io_rights() {
+ exit_bootsafe(&mut daemon, &format!("failed to get I/O permission: {err}"));
+ }
- let input = ProducerHandle::new().expect("ps2d: failed to open input producer");
+ let input = match ProducerHandle::new() {
+ Ok(input) => input,
+ Err(err) => exit_bootsafe(&mut daemon, &format!("failed to open input producer: {err}")),
+ };
@@
- let event_queue: EventQueue<Source> =
- EventQueue::new().expect("ps2d: failed to create event queue");
+ let event_queue: EventQueue<Source> = match EventQueue::new() {
+ Ok(event_queue) => event_queue,
+ Err(err) => exit_bootsafe(&mut daemon, &format!("failed to create event queue: {err}")),
+ };
@@
- .open("/scheme/serio/0")
- .expect("ps2d: failed to open /scheme/serio/0");
+ .open("/scheme/serio/0")
+ .unwrap_or_else(|err| {
+ exit_bootsafe(&mut daemon, &format!("failed to open /scheme/serio/0: {err}"))
+ });
- event_queue
- .subscribe(
- key_file.as_raw_fd() as usize,
- Source::Keyboard,
- event::EventFlags::READ,
- )
- .unwrap();
+ if let Err(err) = event_queue.subscribe(
+ key_file.as_raw_fd() as usize,
+ Source::Keyboard,
+ event::EventFlags::READ,
+ ) {
+ exit_bootsafe(&mut daemon, &format!("failed to subscribe keyboard serio fd: {err}"));
+ }
@@
- .open("/scheme/serio/1")
- .expect("ps2d: failed to open /scheme/serio/1");
+ .open("/scheme/serio/1")
+ .unwrap_or_else(|err| {
+ exit_bootsafe(&mut daemon, &format!("failed to open /scheme/serio/1: {err}"))
+ });
- event_queue
- .subscribe(
- mouse_file.as_raw_fd() as usize,
- Source::Mouse,
- event::EventFlags::READ,
- )
- .unwrap();
+ if let Err(err) = event_queue.subscribe(
+ mouse_file.as_raw_fd() as usize,
+ Source::Mouse,
+ event::EventFlags::READ,
+ ) {
+ exit_bootsafe(&mut daemon, &format!("failed to subscribe mouse serio fd: {err}"));
+ }
@@
- .open(format!("/scheme/time/{}", syscall::CLOCK_MONOTONIC))
- .expect("ps2d: failed to open /scheme/time");
+ .open(format!("/scheme/time/{}", syscall::CLOCK_MONOTONIC))
+ .unwrap_or_else(|err| {
+ exit_bootsafe(&mut daemon, &format!("failed to open /scheme/time: {err}"))
+ });
- event_queue
- .subscribe(
- time_file.as_raw_fd() as usize,
- Source::Time,
- event::EventFlags::READ,
- )
- .unwrap();
+ if let Err(err) = event_queue.subscribe(
+ time_file.as_raw_fd() as usize,
+ Source::Time,
+ event::EventFlags::READ,
+ ) {
+ exit_bootsafe(&mut daemon, &format!("failed to subscribe timer fd: {err}"));
+ }
- libredox::call::setrens(0, 0).expect("ps2d: failed to enter null namespace");
+ if let Err(err) = libredox::call::setrens(0, 0) {
+ exit_bootsafe(&mut daemon, &format!("failed to enter null namespace: {err}"));
+ }
- daemon.ready();
-
- let mut ps2d = Ps2d::new(input, time_file);
+ let mut ps2d = match Ps2d::new(input, time_file) {
+ Ok(ps2d) => ps2d,
+ Err(err) => exit_bootsafe(
+ &mut daemon,
+ &format!("PS/2 controller initialization failed: {err:?}"),
+ ),
+ };
+
+ daemon.take().unwrap().ready();
let mut data = [0; 256];
- for event in event_queue.map(|e| e.expect("ps2d: failed to get next event").user_data) {
+ for event in event_queue {
+ let event = match event {
+ Ok(event) => event.user_data,
+ Err(err) => {
+ error!("ps2d: failed to get next event: {err}");
+ break;
+ }
+ };
// There are some gotchas with ps/2 controllers that require this weird
diff --git a/drivers/input/ps2d/src/state.rs b/drivers/input/ps2d/src/state.rs
--- a/drivers/input/ps2d/src/state.rs
+++ b/drivers/input/ps2d/src/state.rs
@@
-use crate::controller::Ps2;
+use crate::controller::{Error as Ps2Error, Ps2};
@@
impl Ps2d {
- pub fn new(input: ProducerHandle, time_file: File) -> Self {
+ pub fn new(input: ProducerHandle, time_file: File) -> Result<Self, Ps2Error> {
let mut ps2 = Ps2::new();
- ps2.init().expect("failed to initialize");
+ ps2.init()?;
@@
if !this.vmmouse {
// This triggers initializing the mouse
this.handle_mouse(None);
}
- this
+ Ok(this)
}
diff --git a/drivers/usb/xhcid/src/main.rs b/drivers/usb/xhcid/src/main.rs
--- a/drivers/usb/xhcid/src/main.rs
+++ b/drivers/usb/xhcid/src/main.rs
@@
+use std::convert::TryFrom;
use std::fs::File;
+use std::io;
use std::sync::Arc;
use pcid_interface::irq_helpers::read_bsp_apic_id;
#[cfg(target_arch = "x86_64")]
-use pcid_interface::irq_helpers::{
- allocate_first_msi_interrupt_on_bsp, allocate_single_interrupt_vector_for_msi,
-};
+use pcid_interface::irq_helpers::allocate_single_interrupt_vector;
use pcid_interface::{PciFeature, PciFeatureInfo, PciFunctionHandle};
+#[cfg(target_arch = "x86_64")]
+use pcid_interface::{MsiSetFeatureInfo, SetFeatureInfo};
@@
mod usb;
mod xhci;
+#[cfg(target_arch = "x86_64")]
+fn allocate_msix_interrupt(
+ pcid_handle: &mut PciFunctionHandle,
+ msix_info: pcid_interface::msi::MsixInfo,
+) -> io::Result<File> {
+ let mut info = unsafe { msix_info.map_and_mask_all(pcid_handle) };
+ let destination_id = read_bsp_apic_id()?;
+ let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id)?
+ .ok_or_else(|| io::Error::new(io::ErrorKind::WouldBlock, "no interrupt vectors left"))?;
+ let lapic_id = u8::try_from(destination_id)
+ .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "BSP apic id out of range"))?;
+ let msg_addr_and_data = pcid_interface::msi::MsiAddrAndData {
+ addr: pcid_interface::msi::x86::message_address(lapic_id, false, false),
+ data: pcid_interface::msi::x86::message_data_edge_triggered(
+ pcid_interface::msi::x86::DeliveryMode::Fixed,
+ vector,
+ ),
+ };
+
+ let table_entry_pointer = info.table_entry_pointer(0);
+ table_entry_pointer.write_addr_and_data(msg_addr_and_data);
+ table_entry_pointer.unmask();
+
+ pcid_handle.enable_feature(PciFeature::MsiX);
+ log::debug!("Enabled MSI-X");
+
+ Ok(interrupt_handle)
+}
+
+#[cfg(target_arch = "x86_64")]
+fn allocate_msi_interrupt(pcid_handle: &mut PciFunctionHandle) -> io::Result<File> {
+ let destination_id = read_bsp_apic_id()?;
+ let (vector, interrupt_handle) = allocate_single_interrupt_vector(destination_id)?
+ .ok_or_else(|| io::Error::new(io::ErrorKind::WouldBlock, "no interrupt vectors left"))?;
+ let lapic_id = u8::try_from(destination_id)
+ .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "BSP apic id out of range"))?;
+ let set_feature_info = MsiSetFeatureInfo {
+ multi_message_enable: Some(0),
+ message_address_and_data: Some(pcid_interface::msi::MsiAddrAndData {
+ addr: pcid_interface::msi::x86::message_address(lapic_id, false, false),
+ data: pcid_interface::msi::x86::message_data_edge_triggered(
+ pcid_interface::msi::x86::DeliveryMode::Fixed,
+ vector,
+ ),
+ }),
+ mask_bits: None,
+ };
+
+ pcid_handle.set_feature_info(SetFeatureInfo::Msi(set_feature_info));
+ pcid_handle.enable_feature(PciFeature::Msi);
+ log::debug!("Enabled MSI");
+
+ Ok(interrupt_handle)
+}
@@
let has_msi = all_pci_features.iter().any(|feature| feature.is_msi());
let has_msix = all_pci_features.iter().any(|feature| feature.is_msix());
if has_msix {
- let msix_info = match pcid_handle.feature_info(PciFeature::MsiX) {
- PciFeatureInfo::Msi(_) => panic!(),
- PciFeatureInfo::MsiX(s) => s,
- };
- let mut info = unsafe { msix_info.map_and_mask_all(pcid_handle) };
-
- // Allocate one msi vector.
-
- let method = {
- // primary interrupter
- let k = 0;
-
- let table_entry_pointer = info.table_entry_pointer(k);
-
- let destination_id = read_bsp_apic_id().expect("xhcid: failed to read BSP apic id");
- let (msg_addr_and_data, interrupt_handle) =
- allocate_single_interrupt_vector_for_msi(destination_id);
- table_entry_pointer.write_addr_and_data(msg_addr_and_data);
- table_entry_pointer.unmask();
-
- (Some(interrupt_handle), InterruptMethod::Msi)
- };
-
- pcid_handle.enable_feature(PciFeature::MsiX);
- log::debug!("Enabled MSI-X");
-
- method
- } else if has_msi {
- let interrupt_handle = allocate_first_msi_interrupt_on_bsp(pcid_handle);
- (Some(interrupt_handle), InterruptMethod::Msi)
- } else if let Some(irq) = pci_config.func.legacy_interrupt_line {
+ match pcid_handle.feature_info(PciFeature::MsiX) {
+ PciFeatureInfo::MsiX(msix_info) => match allocate_msix_interrupt(pcid_handle, msix_info)
+ {
+ Ok(interrupt_handle) => return (Some(interrupt_handle), InterruptMethod::Msi),
+ Err(err) => {
+ log::warn!("xhcid: MSI-X setup failed, falling back: {err}");
+ }
+ },
+ feature_info => {
+ log::warn!(
+ "xhcid: MSI-X feature probe returned unexpected descriptor {:?}; falling back",
+ feature_info
+ );
+ }
+ }
+ }
+
+ if has_msi {
+ match allocate_msi_interrupt(pcid_handle) {
+ Ok(interrupt_handle) => return (Some(interrupt_handle), InterruptMethod::Msi),
+ Err(err) => {
+ log::warn!("xhcid: MSI setup failed, falling back: {err}");
+ }
+ }
+ }
+
+ if let Some(irq) = pci_config.func.legacy_interrupt_line {
log::debug!("Legacy IRQ {}", irq);
@@
log::info!("XHCI {}", pci_config.func.display());
let scheme_name = format!("usb.{}", name);
- let socket = Socket::create().expect("xhcid: failed to create usb scheme");
+ let socket = match Socket::create() {
+ Ok(socket) => socket,
+ Err(err) => {
+ log::error!("xhcid: failed to create usb scheme: {err}");
+ std::process::exit(0);
+ }
+ };
let mut state = SchemeState::new();
- let hci = Arc::new(
- Xhci::<N>::new(scheme_name.clone(), address, interrupt_method, pcid_handle)
- .expect("xhcid: failed to allocate device"),
- );
- register_sync_scheme(&socket, &scheme_name, &mut &*hci)
- .expect("xhcid: failed to regsiter scheme to namespace");
+ let hci = match Xhci::<N>::new(scheme_name.clone(), address, interrupt_method, pcid_handle) {
+ Ok(hci) => Arc::new(hci),
+ Err(err) => {
+ log::error!("xhcid: failed to allocate device: {err}");
+ std::process::exit(0);
+ }
+ };
+ if let Err(err) = register_sync_scheme(&socket, &scheme_name, &mut &*hci) {
+ log::error!("xhcid: failed to register scheme to namespace: {err}");
+ std::process::exit(0);
+ }
diff --git a/drivers/usb/xhcid/src/xhci/device_enumerator.rs b/drivers/usb/xhcid/src/xhci/device_enumerator.rs
--- a/drivers/usb/xhcid/src/xhci/device_enumerator.rs
+++ b/drivers/usb/xhcid/src/xhci/device_enumerator.rs
@@
impl<const N: usize> DeviceEnumerator<N> {
+ fn is_port_disabled_state(flags: &PortFlags) -> bool {
+ flags.contains(PortFlags::PP)
+ && flags.contains(PortFlags::CCS)
+ && !flags.contains(PortFlags::PED)
+ && !flags.contains(PortFlags::PR)
+ }
+
+ fn wait_for_stable_port_flags(
+ &self,
+ port_id: PortId,
+ mut flags: PortFlags,
+ ) -> Option<PortFlags> {
+ const MAX_ATTEMPTS: usize = 8;
+ const STABILIZE_DELAY: Duration = Duration::from_millis(20);
+
+ if flags.contains(PortFlags::PED) || Self::is_port_disabled_state(&flags) {
+ return Some(flags);
+ }
+
+ for attempt in 0..MAX_ATTEMPTS {
+ debug!(
+ "Port {} reported transient flags {:?}; waiting for a stable state ({}/{})",
+ port_id,
+ flags,
+ attempt + 1,
+ MAX_ATTEMPTS
+ );
+ std::thread::sleep(STABILIZE_DELAY);
+
+ flags = {
+ let ports = self.hci.ports.lock().unwrap();
+ let index = port_id.root_hub_port_index();
+ if index >= ports.len() {
+ warn!(
+ "Port {} disappeared while waiting for a stable state",
+ port_id
+ );
+ return None;
+ }
+ ports[index].flags()
+ };
+
+ if flags.contains(PortFlags::PED) || Self::is_port_disabled_state(&flags) {
+ return Some(flags);
+ }
+ }
+
+ None
+ }
+
pub fn new(hci: Arc<Xhci<N>>) -> Self {
let request_queue = hci.device_enumerator_receiver.clone();
DeviceEnumerator { hci, request_queue }
}
@@
loop {
debug!("Start Device Enumerator Loop");
let request = match self.request_queue.recv() {
Ok(req) => req,
- Err(err) => {
- panic!("Failed to received an enumeration request! error: {}", err)
- }
+ Err(err) => {
+ warn!("xhcid: device enumerator shutting down: {}", err);
+ return;
+ }
};
@@
- if flags.contains(PortFlags::CCS) {
+ if flags.contains(PortFlags::CCS) {
+ let Some(flags) = self.wait_for_stable_port_flags(port_id, flags) else {
+ warn!(
+ "Port {} never reached a stable connected state; ignoring this change",
+ port_id
+ );
+ continue;
+ };
+
debug!(
"Received Device Connect Port Status Change Event with port flags {:?}",
flags
);
@@
//If the port isn't enabled (i.e. it's a USB2 port), we need to reset it if it isn't resetting already
//A USB3 port won't generate a Connect Status Change until it's already enabled, so this check
//will always be skipped for USB3 ports
if !flags.contains(PortFlags::PED) {
- let disabled_state = flags.contains(PortFlags::PP)
- && flags.contains(PortFlags::CCS)
- && !flags.contains(PortFlags::PED)
- && !flags.contains(PortFlags::PR);
-
- if !disabled_state {
- panic!(
- "Port {} isn't in the disabled state! Current flags: {:?}",
- port_id, flags
- );
- } else {
- debug!("Port {} has entered the disabled state.", port_id);
- }
+ debug!("Port {} has entered the disabled state.", port_id);
@@
if !enabled_state {
warn!(
"Port {} isn't in the enabled state! Current flags: {:?}",
port_id, flags
);
- } else {
- debug!(
- "Port {} is in the enabled state. Proceeding with enumeration",
- port_id
- );
+ continue;
}
+ debug!(
+ "Port {} is in the enabled state. Proceeding with enumeration",
+ port_id
+ );
}
diff --git a/drivers/usb/xhcid/src/xhci/irq_reactor.rs b/drivers/usb/xhcid/src/xhci/irq_reactor.rs
--- a/drivers/usb/xhcid/src/xhci/irq_reactor.rs
+++ b/drivers/usb/xhcid/src/xhci/irq_reactor.rs
@@
let port_id = PortId {
root_hub_port_num,
route_string: 0,
};
trace!("Received Port Status Change Request on port {}", port_id);
- self.device_enumerator_sender
- .send(DeviceEnumerationRequest { port_id })
- .expect(
- format!(
- "Failed to transmit device numeration request on port {}",
- port_id
- )
- .as_str(),
- );
+ if let Err(err) = self
+ .device_enumerator_sender
+ .send(DeviceEnumerationRequest { port_id })
+ {
+ warn!(
+ "Failed to transmit device enumeration request on port {}: {}",
+ port_id,
+ err
+ );
+ return;
+ }
{
let mut ports = self.hci.ports.lock().unwrap();