From 20e6384cca5218bd411fbe3c59dcbc2a9f4532dd Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Wed, 10 Mar 2021 20:50:16 +0100 Subject: [PATCH] WIP: Implement the userspace ACPI scheme. --- acpid/src/acpi.rs | 8 +- acpid/src/main.rs | 9 +- acpid/src/scheme.rs | 282 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 274 insertions(+), 25 deletions(-) diff --git a/acpid/src/acpi.rs b/acpid/src/acpi.rs index 4a697a6fc3..ddf3061229 100644 --- a/acpid/src/acpi.rs +++ b/acpid/src/acpi.rs @@ -38,7 +38,7 @@ impl SdtHeader { oem_table_id: self.oem_table_id, } } - fn length(&self) -> usize { + pub fn length(&self) -> usize { self .length .try_into() @@ -172,6 +172,9 @@ impl Sdt { Self::new(loaded.into()).map_err(Into::into) } + pub fn as_slice(&self) -> &[u8] { + &self.0 + } } impl Deref for Sdt { @@ -276,6 +279,9 @@ impl AcpiContext { pub fn get_index_from_signature(&self, signature: &SdtSignature) -> Option { todo!() } + pub fn tables(&self) -> &[Sdt] { + &self.tables + } } #[repr(packed)] diff --git a/acpid/src/main.rs b/acpid/src/main.rs index 2efa91161e..3d34315b6a 100644 --- a/acpid/src/main.rs +++ b/acpid/src/main.rs @@ -1,4 +1,4 @@ -#![feature(renamed_spin_loop)] +#![feature(renamed_spin_loop, seek_convenience)] use std::convert::{TryFrom, TryInto}; use std::io::prelude::*; @@ -8,7 +8,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::sync::Arc; use redox_log::RedoxLogger; -use syscall::scheme::Scheme; +use syscall::scheme::SchemeMut; use syscall::data::{Event, Packet}; use syscall::flag::EventFlags; @@ -97,6 +97,7 @@ fn main() { let exit_code = if bytes_read == res.len() { res[0] } else { + eprintln!("acpid: daemon pipe EOF"); 1 }; drop(read_part); @@ -175,7 +176,7 @@ fn main() { data: 1, }).expect("acpid: failed to register scheme socket for event queue"); - let scheme = self::scheme::AcpiScheme::new(&acpi_context); + let mut scheme = self::scheme::AcpiScheme::new(&acpi_context); let mut event = Event::default(); let mut packet = Packet::default(); @@ -186,7 +187,7 @@ fn main() { if event.flags.contains(EventFlags::EVENT_READ) && event.id == shutdown_pipe.as_raw_fd() as usize { break; } - if !event.flags.contains(EventFlags::EVENT_NONE) || event.id != scheme_socket.as_raw_fd() as usize { + if !event.flags.contains(EventFlags::EVENT_READ) || event.id != scheme_socket.as_raw_fd() as usize { continue; } diff --git a/acpid/src/scheme.rs b/acpid/src/scheme.rs index ac2bff38c2..2587a1c949 100644 --- a/acpid/src/scheme.rs +++ b/acpid/src/scheme.rs @@ -1,37 +1,279 @@ -use syscall::error::{EBADF, ENOENT}; -use syscall::error::{Error, Result}; -use syscall::scheme::Scheme; +use std::collections::BTreeMap; +use std::convert::{TryFrom, TryInto}; -use crate::acpi::AcpiContext; +use syscall::data::Stat; +use syscall::error::{EBADF, EBADFD, EINVAL, EISDIR, ENOENT, ENOTDIR, EOVERFLOW}; +use syscall::error::{Error, Result}; +use syscall::flag::{O_ACCMODE, O_DIRECTORY, O_RDONLY, O_STAT, O_SYMLINK}; +use syscall::flag::{MODE_FILE, MODE_DIR, SEEK_CUR, SEEK_END, SEEK_SET}; +use syscall::scheme::SchemeMut; + +use crate::acpi::{AcpiContext, SdtSignature}; pub struct AcpiScheme<'acpi> { ctx: &'acpi AcpiContext, + handles: BTreeMap, + next_fd: usize, +} + +struct Handle { + offset: usize, + kind: HandleKind, + stat: bool, +} +enum HandleKind { + TopLevel, + Tables, + Table(SdtSignature), +} + +impl HandleKind { + fn is_dir(&self) -> bool { + match self { + Self::TopLevel => true, + Self::Tables => true, + Self::Table(_) => false, + } + } + fn len(&self, acpi_ctx: &AcpiContext) -> Result { + Ok(match self { + Self::TopLevel => TOPLEVEL_CONTENTS.len(), + Self::Tables => acpi_ctx.tables().len().checked_mul(TABLE_DENTRY_LENGTH).unwrap_or(usize::max_value()), + Self::Table(signature) => acpi_ctx.sdt_from_signature(signature).ok_or(Error::new(EBADFD))?.length(), + }) + } } impl<'acpi> AcpiScheme<'acpi> { pub fn new(ctx: &'acpi AcpiContext) -> Self { Self { ctx, + handles: BTreeMap::new(), + next_fd: 0, } } } -const ALLOWED_TABLE_SIGNATURES: [[u8; 4]; 1] = [*b"MCFG"]; +const TOPLEVEL_CONTENTS: &[u8] = b"tables\n"; -impl Scheme for AcpiScheme<'_> { - fn open(&self, path: &str, flags: usize, uid: u32, gid: u32) -> Result { - Err(Error::new(ENOENT)) - } - fn seek(&self, id: usize, pos: isize, whence: usize) -> Result { - Err(Error::new(EBADF)) - } - fn read(&self, id: usize, buf: &mut [u8]) -> Result { - Err(Error::new(EBADF)) - } - fn write(&self, id: usize, buf: &[u8]) -> Result { - Err(Error::new(EBADF)) - } - fn close(&self, id: usize) -> Result { - Err(Error::new(EBADF)) +const TABLE_DENTRY_LENGTH: usize = 35; + +fn parse_hex_digit(hex: u8) -> Option { + let hex = hex.to_ascii_lowercase(); + + if hex >= b'a' && hex <= b'f' { + Some(hex - b'a') + } else if hex >= b'0' && hex <= b'9' { + Some(hex - b'0') + } else { + None + } +} + +fn parse_hex_2digit(hex: &[u8]) -> Option { + parse_hex_digit(hex[0]).and_then(|least_significant| Some(least_significant | (parse_hex_digit(hex[1])? << 4))) +} + +fn parse_oem_id(hex: [u8; 12]) -> Option<[u8; 6]> { + Some([ + parse_hex_2digit(&hex[0..2])?, + parse_hex_2digit(&hex[2..4])?, + parse_hex_2digit(&hex[4..6])?, + parse_hex_2digit(&hex[6..8])?, + parse_hex_2digit(&hex[8..10])?, + parse_hex_2digit(&hex[10..12])?, + ]) +} +fn parse_oem_table_id(hex: [u8; 16]) -> Option<[u8; 8]> { + Some([ + parse_hex_2digit(&hex[0..2])?, + parse_hex_2digit(&hex[2..4])?, + parse_hex_2digit(&hex[4..6])?, + parse_hex_2digit(&hex[6..8])?, + parse_hex_2digit(&hex[8..10])?, + parse_hex_2digit(&hex[10..12])?, + parse_hex_2digit(&hex[12..14])?, + parse_hex_2digit(&hex[14..16])?, + ]) +} + +fn parse_table(table: &[u8]) -> Option { + let signature_part = table.get(..4)?; + let first_hyphen = table.get(5)?; + let oem_id_part = table.get(5..17)?; + let second_hyphen = table.get(17)?; + let oem_table_part = table.get(18..34)?; + + if *first_hyphen != b'-' { + return None; + } + if *second_hyphen != b'-' { + return None; + } + + if table.len() > 20 { + return None; + } + + Some(SdtSignature { + signature: <[u8; 4]>::try_from(signature_part).expect("expected 4-byte slice to be convertible into [u8; 4]"), + oem_id: { + let hex = <[u8; 12]>::try_from(oem_id_part).expect("expected 12-byte slice to be convertible into [u8; 12]"); + parse_oem_id(hex)? + }, + oem_table_id: { + let hex = <[u8; 16]>::try_from(oem_table_part).expect("expected 16-byte slice to be convertible into [u8; 16]"); + parse_oem_table_id(hex)? + }, + }) +} + +impl SchemeMut for AcpiScheme<'_> { + fn open(&mut self, path: &str, flags: usize, _uid: u32, _gid: u32) -> Result { + let path = path.trim_start_matches('/'); + + let flag_stat = flags & O_STAT == O_STAT; + let flag_dir = flags & O_DIRECTORY == O_DIRECTORY; + + // TODO: arrayvec + let components = path.split('/').collect::>(); + + let kind = match &*components { + [] => HandleKind::TopLevel, + ["tables"] => HandleKind::Tables, + + ["tables", table] => { + let signature = parse_table(table.as_bytes()).ok_or(Error::new(ENOENT))?; + HandleKind::Table(signature) + } + + _ => return Err(Error::new(ENOENT)), + }; + + if kind.is_dir() && !flag_dir && !flag_stat { + return Err(Error::new(EISDIR)); + } else if flag_dir && !flag_stat { + return Err(Error::new(ENOTDIR)); + } + + if flags & O_ACCMODE != O_RDONLY && !flag_stat { + return Err(Error::new(EINVAL)); + } + + if flags & O_SYMLINK == O_SYMLINK && !flag_stat { + return Err(Error::new(EINVAL)); + } + + let fd = self.next_fd; + self.next_fd += 1; + + self.handles.insert(fd, Handle { + offset: 0, + stat: flag_stat, + kind, + }); + + Ok(fd) + } + fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result { + let handle = self.handles.get(&id).ok_or(Error::new(EBADF))?; + + stat.st_size = handle.kind.len(self.ctx)?.try_into().unwrap_or(u64::max_value()); + + if handle.kind.is_dir() { + stat.st_mode = MODE_DIR; + } else { + stat.st_mode = MODE_FILE; + } + + Ok(0) + } + fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + let file_len = handle.kind.len(self.ctx)?; + + let new_offset = match whence { + SEEK_SET => pos as usize, + SEEK_CUR => if pos < 0 { + handle.offset.checked_sub((-pos) as usize).ok_or(Error::new(EINVAL))? + } else { + handle.offset.saturating_add(pos as usize) + }, + SEEK_END => if pos < 0 { + file_len.checked_sub((-pos) as usize).ok_or(Error::new(EINVAL))? + } else { + file_len + } + + _ => return Err(Error::new(EINVAL)), + }; + + handle.offset = new_offset; + Ok(new_offset as isize) + } + fn read(&mut self, id: usize, buf: &mut [u8]) -> Result { + let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + + let src_buf = match handle.kind { + HandleKind::TopLevel => TOPLEVEL_CONTENTS, + HandleKind::Table(ref signature) => self.ctx.sdt_from_signature(signature).ok_or(Error::new(EBADFD))?.as_slice(), + + HandleKind::Tables => { + use std::io::prelude::*; + + let tables_to_skip = handle.offset / TABLE_DENTRY_LENGTH; + let tables_to_fill = (buf.len() + TABLE_DENTRY_LENGTH - 1) / TABLE_DENTRY_LENGTH; + + let bytes_to_skip = handle.offset % TABLE_DENTRY_LENGTH; + + let mut src_buf = [0_u8; TABLE_DENTRY_LENGTH]; + let mut bytes_written = 0; + + for table in self.ctx.tables().iter().skip(tables_to_skip).take(tables_to_fill) { + let mut cursor = std::io::Cursor::new(&mut src_buf[..]); + cursor.write_all(&table.signature).unwrap(); + cursor.write_all(&[b'-']).unwrap(); + // TODO: Treat these IDs as strings? + for byte in table.oem_id.iter() { + write!(cursor, "{:>02X}", byte).unwrap(); + } + cursor.write_all(&[b'-']).unwrap(); + for byte in table.oem_table_id.iter() { + write!(cursor, "{:>02X}", byte).unwrap(); + } + + let src_buf = &src_buf[bytes_to_skip..]; + let dst_buf = &mut buf[bytes_written..]; + let to_copy = std::cmp::min(src_buf.len(), dst_buf.len()); + dst_buf[..to_copy].copy_from_slice(&src_buf[..to_copy]); + bytes_written += to_copy; + } + + handle.offset = handle.offset.checked_add(bytes_written).ok_or(Error::new(EOVERFLOW))?; + + return Ok(bytes_written); + } + }; + + let offset = std::cmp::min(src_buf.len(), handle.offset); + let src_buf = &src_buf[offset..]; + + let to_copy = std::cmp::min(src_buf.len(), buf.len()); + buf[..to_copy].copy_from_slice(&src_buf[..to_copy]); + + handle.offset += to_copy; + + Ok(to_copy) + } + fn write(&mut self, _id: usize, _buf: &[u8]) -> Result { + Err(Error::new(EBADF)) + } + fn close(&mut self, id: usize) -> Result { + if self.handles.remove(&id).is_none() { + return Err(Error::new(EBADF)); + } + + Ok(0) } }