From 5fc0f0fa4ec3ffc5307f75aa51ad6152437d2b52 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Thu, 11 Mar 2021 12:48:34 +0100 Subject: [PATCH] Successfully parse the namespace. --- acpid/src/acpi.rs | 70 +++++++++++++++++++++++++++--------- acpid/src/aml/mod.rs | 32 ++++++----------- acpid/src/aml/namespace.rs | 3 +- acpid/src/aml/parser.rs | 1 + acpid/src/aml/type1opcode.rs | 20 +++++------ acpid/src/aml/type2opcode.rs | 8 ++--- acpid/src/scheme.rs | 28 ++++++++++----- 7 files changed, 98 insertions(+), 64 deletions(-) diff --git a/acpid/src/acpi.rs b/acpid/src/acpi.rs index ddf3061229..4d08cfe919 100644 --- a/acpid/src/acpi.rs +++ b/acpid/src/acpi.rs @@ -1,7 +1,7 @@ -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use std::convert::{TryFrom, TryInto}; use std::ops::Deref; -use std::sync::Arc; +use std::sync::{Arc, atomic::{self, AtomicUsize}}; use std::{fmt, mem}; use syscall::flag::PhysmapFlags; @@ -46,13 +46,19 @@ impl SdtHeader { } } -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub struct SdtSignature { pub signature: [u8; 4], pub oem_id: [u8; 6], pub oem_table_id: [u8; 8], } +impl fmt::Display for SdtSignature { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}-{}-{}", String::from_utf8_lossy(&self.signature), String::from_utf8_lossy(&self.oem_id), String::from_utf8_lossy(&self.oem_table_id)) + } +} + #[derive(Debug, Error)] pub enum TablePhysLoadError { // TODO: Make syscall::Error implement std::error::Error, when enabling a Cargo feature. @@ -208,7 +214,17 @@ pub struct AcpiContext { tables: Vec, dsdt: Option, fadt: Option, + + // TODO: Remove Option. This is not kernel code, and not static, but we still need to replace + // every match where namespace{,_mut}() are used. namespace: RwLock>>, + + // TODO: The kernel ACPI code seemed to use load_table quite ubiquitously, however ACPI 5.1 + // states that DDBHandles can only be obtained when loading XSDT-pointed tables. So, we'll + // generate an index only for those. + + sdt_order: RwLock>>, + pub next_ctx: RwLock, } impl AcpiContext { @@ -228,12 +244,20 @@ impl AcpiContext { tables, dsdt: None, fadt: None, - namespace: RwLock::new(None), + namespace: RwLock::new(Some(HashMap::new())), next_ctx: RwLock::new(0), + + sdt_order: RwLock::new(Vec::new()), }; + for table in &this.tables { + this.new_index(&table.signature()); + } + Fadt::init(&mut this); + crate::aml::init_namespace(&this); + this } @@ -252,9 +276,6 @@ impl AcpiContext { self.tables.iter().position(|sdt| sdt.signature == signature) } - pub fn find_single_sdt(&self, signature: [u8; 4]) -> Option<&Sdt> { - self.find_single_sdt_pos(signature).map(|pos| &self.tables[pos]) - } pub fn find_multiple_sdts<'a>(&'a self, signature: [u8; 4]) -> impl Iterator { self.tables.iter().filter(move |sdt| sdt.signature == signature) } @@ -274,14 +295,17 @@ impl AcpiContext { self.tables.iter().find(|sdt| sdt.signature == signature.signature && sdt.oem_id == signature.oem_id && sdt.oem_table_id == signature.oem_table_id) } pub fn get_signature_from_index(&self, index: usize) -> Option { - todo!() + self.sdt_order.read().get(index).copied().flatten() } pub fn get_index_from_signature(&self, signature: &SdtSignature) -> Option { - todo!() + self.sdt_order.read().iter().rposition(|sig| sig.map_or(false, |sig| &sig == signature)) } pub fn tables(&self) -> &[Sdt] { &self.tables } + pub fn new_index(&self, signature: &SdtSignature) { + self.sdt_order.write().push(Some(*signature)); + } } #[repr(packed)] @@ -393,7 +417,7 @@ impl Deref for Fadt { } impl Fadt { - pub fn new(context: &AcpiContext, sdt: Sdt) -> Option { + pub fn new(sdt: Sdt) -> Option { if sdt.signature != *b"FACP" || sdt.length() < mem::size_of::() { return None; } @@ -405,7 +429,7 @@ impl Fadt { .take_single_sdt(*b"FACP") .expect("expected ACPI to always have a FADT"); - let fadt = match Fadt::new(context, fadt_sdt) { + let fadt = match Fadt::new(fadt_sdt) { Some(fadt) => fadt, None => { log::error!("Failed to find FADT"); @@ -432,13 +456,9 @@ impl Fadt { } }; - context.dsdt = Some(Dsdt(dsdt_sdt)); + context.dsdt = Some(Dsdt(dsdt_sdt.clone())); - /* - let signature = get_sdt_signature(dsdt_sdt); - if let Some(ref mut ptrs) = *(SDT_POINTERS.write()) { - ptrs.insert(signature, dsdt_sdt); - }*/ + context.tables.push(dsdt_sdt); } } @@ -462,10 +482,17 @@ impl AmlContainingTable for PossibleAmlTables { Self::Ssdt(ssdt) => ssdt.aml(), } } + fn header(&self) -> &SdtHeader { + match self { + Self::Dsdt(dsdt) => dsdt.header(), + Self::Ssdt(ssdt) => ssdt.header(), + } + } } pub trait AmlContainingTable { fn aml(&self) -> &[u8]; + fn header(&self) -> &SdtHeader; } impl AmlContainingTable for &T @@ -475,15 +502,24 @@ where fn aml(&self) -> &[u8] { T::aml(*self) } + fn header(&self) -> &SdtHeader { + T::header(*self) + } } impl AmlContainingTable for Dsdt { fn aml(&self) -> &[u8] { self.0.data() } + fn header(&self) -> &SdtHeader { + &*self.0 + } } impl AmlContainingTable for Ssdt { fn aml(&self) -> &[u8] { self.0.data() } + fn header(&self) -> &SdtHeader { + &*self.0 + } } diff --git a/acpid/src/aml/mod.rs b/acpid/src/aml/mod.rs index fa808729f7..b48797e493 100644 --- a/acpid/src/aml/mod.rs +++ b/acpid/src/aml/mod.rs @@ -49,34 +49,24 @@ pub fn parse_aml_with_scope(acpi_ctx: &AcpiContext, sdt: impl AmlContainingTable Ok(ctx.namespace_delta) } -pub fn is_aml_table(sdt: &SdtHeader) -> bool { - if &sdt.signature == b"DSDT" || &sdt.signature == b"SSDT" { - true - } else { - false - } -} - fn init_aml_table(acpi_ctx: &AcpiContext, sdt: impl AmlContainingTable) { - match parse_aml_table(acpi_ctx, sdt) { - Ok(_) => println!(": Parsed"), - Err(AmlError::AmlParseError(e)) => println!(": {}", e), - Err(AmlError::AmlInvalidOpCode) => println!(": Invalid opcode"), - Err(AmlError::AmlValueError) => println!(": Type constraints or value bounds not met"), - Err(AmlError::AmlDeferredLoad) => println!(": Deferred load reached top level"), - Err(AmlError::AmlFatalError(_, _, _)) => { - println!(": Fatal error occurred"); - // TODO + match parse_aml_table(acpi_ctx, &sdt) { + Ok(_) => log::info!("Table {} parsed successfully", sdt.header().signature()), + Err(AmlError::AmlParseError(e)) => log::error!("Table {} got parse error: {}", sdt.header().signature(), e), + Err(AmlError::AmlInvalidOpCode) => log::error!("Table {} got invalid opcode", sdt.header().signature()), + Err(AmlError::AmlValueError) => log::error!("For table {}: type constraints or value bounds not met", sdt.header().signature()), + Err(AmlError::AmlDeferredLoad) => log::error!("For table {}: deferred load reached top level", sdt.header().signature()), + Err(AmlError::AmlFatalError(ty, code, val)) => { + log::error!("Fatal error occurred for table {}: type={}, code={}, val={:?}", sdt.header().signature(), ty, code, val); return; }, Err(AmlError::AmlHardFatal) => { - println!(": Fatal error occurred"); - // TODO + log::error!("Hard fatal error occurred for table {}", sdt.header().signature()); return; } } } -fn init_namespace(context: &AcpiContext) -> HashMap { +pub fn init_namespace(context: &AcpiContext) { let dsdt = context.dsdt().expect("could not find any DSDT"); log::info!("Found DSDT."); @@ -88,8 +78,6 @@ fn init_namespace(context: &AcpiContext) -> HashMap { print!("Found SSDT."); init_aml_table(context, ssdt); } - - todo!() } pub fn set_global_s_state(context: &AcpiContext, state: u8) { diff --git a/acpid/src/aml/namespace.rs b/acpid/src/aml/namespace.rs index 94938dcce9..600a253a56 100644 --- a/acpid/src/aml/namespace.rs +++ b/acpid/src/aml/namespace.rs @@ -253,9 +253,10 @@ impl AmlValue { AmlValue::Integer(i) => if let Some(sig) = acpi_ctx.get_signature_from_index(i as usize) { Ok((vec!(), sig)) } else { + log::error!("AmlValueError in get_as_ddb_handle integer"); Err(AmlError::AmlValueError) }, - _ => Err(AmlError::AmlValueError) + _ => { log::error!("AmlValueError in get_as_ddb_handle other"); Err(AmlError::AmlValueError) } } } diff --git a/acpid/src/aml/parser.rs b/acpid/src/aml/parser.rs index be0a349f2e..704aee5d8c 100644 --- a/acpid/src/aml/parser.rs +++ b/acpid/src/aml/parser.rs @@ -10,6 +10,7 @@ use crate::acpi::AcpiContext; pub type ParseResult = Result; pub type AmlParseType = AmlParseTypeGeneric; +#[derive(Debug)] pub struct AmlParseTypeGeneric { pub val: T, pub len: usize diff --git a/acpid/src/aml/type1opcode.rs b/acpid/src/aml/type1opcode.rs index cd61a78d71..b89d939cb3 100644 --- a/acpid/src/aml/type1opcode.rs +++ b/acpid/src/aml/type1opcode.rs @@ -1,5 +1,3 @@ -use std::mem; - use super::AmlError; use super::parser::{AmlParseType, ParseResult, AmlExecutionContext, ExecutionState}; use super::namespace::AmlValue; @@ -9,8 +7,8 @@ use super::namestring::{parse_name_string, parse_super_name}; use crate::monotonic; -use crate::acpi::{PossibleAmlTables, SdtHeader}; -use super::{parse_aml_table, is_aml_table}; +use crate::acpi::PossibleAmlTables; +use super::parse_aml_table; pub fn parse_type1_opcode(data: &[u8], ctx: &mut AmlExecutionContext) -> ParseResult { @@ -154,23 +152,23 @@ fn parse_def_load(data: &[u8], let ddb_handle_object = parse_super_name(&data[2 + name.len..], ctx)?; let tbl = ctx.get(ctx.acpi_context(), name.val)?.get_as_buffer(ctx.acpi_context())?; - // TODO - let sdt = plain::from_bytes::(&tbl[..mem::size_of::()]).unwrap(); - let table = match ctx.acpi_context().sdt_from_signature(&sdt.signature()) { - Some(table) => table, - None => return Err(AmlError::AmlValueError), - }; + let table = crate::acpi::Sdt::new(tbl.into()).map_err(|error| { + log::error!("Failed to parse def_load ACPI table: {}", error); + AmlError::AmlValueError + })?; if let Some(aml_sdt) = PossibleAmlTables::try_new(table.clone()) { + ctx.acpi_context().new_index(&table.signature()); let delta = parse_aml_table(ctx.acpi_context(), aml_sdt)?; - let _ = ctx.modify(ctx.acpi_context(), ddb_handle_object.val, AmlValue::DDBHandle((delta, sdt.signature()))); + let _ = ctx.modify(ctx.acpi_context(), ddb_handle_object.val, AmlValue::DDBHandle((delta, table.signature()))); Ok(AmlParseType { val: AmlValue::None, len: 2 + name.len + ddb_handle_object.len }) } else { + log::error!("Loaded table is not for AML"); Err(AmlError::AmlValueError) } } diff --git a/acpid/src/aml/type2opcode.rs b/acpid/src/aml/type2opcode.rs index ec137cdeb3..60d02bb717 100644 --- a/acpid/src/aml/type2opcode.rs +++ b/acpid/src/aml/type2opcode.rs @@ -1346,15 +1346,15 @@ fn parse_def_load_table(data: &[u8], let parameter_data = parse_term_arg(&data[2 + signature.len + oem_id.len + oem_table_id.len + root_path.len + parameter_path.len..], ctx)?; let signature = { - <[u8; 4]>::try_from(&*signature.val.get_as_buffer(ctx.acpi_context())?) + <[u8; 4]>::try_from(&*signature.val.get_as_string(ctx.acpi_context())?.as_bytes()) .expect("expected 'load table' def to have a signature that is 4 bytes long") }; let oem_id = { - <[u8; 6]>::try_from(&*oem_id.val.get_as_buffer(ctx.acpi_context())?) + <[u8; 6]>::try_from(&*oem_id.val.get_as_string(ctx.acpi_context())?.as_bytes()) .expect("expected 'load table' def to have an OEM ID that is 6 bytes long") }; let oem_table_id = { - <[u8; 8]>::try_from(&*oem_table_id.val.get_as_buffer(ctx.acpi_context())?) + <[u8; 8]>::try_from(&*oem_table_id.val.get_as_string(ctx.acpi_context())?.as_bytes()) .expect("expected 'load table' def to have an OEM table ID that is 8 bytes long") }; @@ -1594,7 +1594,7 @@ fn parse_def_mid(data: &[u8], let mut res = s.clone().split_off(idx); if len < res.len() { - res.split_off(len); + res.truncate(len); } AmlValue::String(res) diff --git a/acpid/src/scheme.rs b/acpid/src/scheme.rs index 2587a1c949..3aeafc41ec 100644 --- a/acpid/src/scheme.rs +++ b/acpid/src/scheme.rs @@ -62,7 +62,7 @@ fn parse_hex_digit(hex: u8) -> Option { let hex = hex.to_ascii_lowercase(); if hex >= b'a' && hex <= b'f' { - Some(hex - b'a') + Some(hex - b'a' + 10) } else if hex >= b'0' && hex <= b'9' { Some(hex - b'0') } else { @@ -71,7 +71,7 @@ fn parse_hex_digit(hex: u8) -> Option { } 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))) + parse_hex_digit(hex[0]).and_then(|most_significant| Some((most_significant << 4) | parse_hex_digit(hex[1])?)) } fn parse_oem_id(hex: [u8; 12]) -> Option<[u8; 6]> { @@ -99,7 +99,7 @@ fn parse_oem_table_id(hex: [u8; 16]) -> Option<[u8; 8]> { fn parse_table(table: &[u8]) -> Option { let signature_part = table.get(..4)?; - let first_hyphen = table.get(5)?; + let first_hyphen = table.get(4)?; let oem_id_part = table.get(5..17)?; let second_hyphen = table.get(17)?; let oem_table_part = table.get(18..34)?; @@ -111,7 +111,7 @@ fn parse_table(table: &[u8]) -> Option { return None; } - if table.len() > 20 { + if table.len() > 34 { return None; } @@ -139,7 +139,7 @@ impl SchemeMut for AcpiScheme<'_> { let components = path.split('/').collect::>(); let kind = match &*components { - [] => HandleKind::TopLevel, + [""] => HandleKind::TopLevel, ["tables"] => HandleKind::Tables, ["tables", table] => { @@ -152,7 +152,7 @@ impl SchemeMut for AcpiScheme<'_> { if kind.is_dir() && !flag_dir && !flag_stat { return Err(Error::new(EISDIR)); - } else if flag_dir && !flag_stat { + } else if !kind.is_dir() && flag_dir && !flag_stat { return Err(Error::new(ENOTDIR)); } @@ -191,6 +191,10 @@ impl SchemeMut for AcpiScheme<'_> { fn seek(&mut self, id: usize, pos: isize, whence: usize) -> Result { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + if handle.stat { + return Err(Error::new(EBADF)); + } + let file_len = handle.kind.len(self.ctx)?; let new_offset = match whence { @@ -215,6 +219,10 @@ impl SchemeMut for AcpiScheme<'_> { fn read(&mut self, id: usize, buf: &mut [u8]) -> Result { let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?; + if handle.stat { + return Err(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(), @@ -223,14 +231,14 @@ impl SchemeMut for AcpiScheme<'_> { 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 max_tables_to_fill = (buf.len() + TABLE_DENTRY_LENGTH - 1) / TABLE_DENTRY_LENGTH; - let bytes_to_skip = handle.offset % TABLE_DENTRY_LENGTH; + let mut 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) { + for table in self.ctx.tables().iter().skip(tables_to_skip).take(max_tables_to_fill) { let mut cursor = std::io::Cursor::new(&mut src_buf[..]); cursor.write_all(&table.signature).unwrap(); cursor.write_all(&[b'-']).unwrap(); @@ -242,12 +250,14 @@ impl SchemeMut for AcpiScheme<'_> { for byte in table.oem_table_id.iter() { write!(cursor, "{:>02X}", byte).unwrap(); } + cursor.write_all(&[b'\n']).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; + bytes_to_skip = 0; } handle.offset = handle.offset.checked_add(bytes_written).ok_or(Error::new(EOVERFLOW))?;