Explorar o código

Handle writes to operation regions

This code is still pretty janky and not totally correct, but it fixed up
more at a later date when I have more time. For now, this handles what we
actually need to do.
Isaac Woods %!s(int64=4) %!d(string=hai) anos
pai
achega
d550f5b95f
Modificáronse 3 ficheiros con 172 adicións e 8 borrados
  1. 110 2
      aml/src/lib.rs
  2. 53 6
      aml/src/value.rs
  3. 9 0
      aml_tester/src/main.rs

+ 110 - 2
aml/src/lib.rs

@@ -330,11 +330,23 @@ impl AmlContext {
     /// be altered during a store in some circumstances. If the target is a `Name`, this also performs required
     /// implicit conversions. Stores to other targets are semantically equivalent to a `CopyObject`.
     pub(crate) fn store(&mut self, target: Target, value: AmlValue) -> Result<AmlValue, AmlError> {
+        use value::AmlType;
+
         match target {
             Target::Name(ref path) => {
                 let (_, handle) = self.namespace.search(path, &self.current_scope)?;
-                let desired_type = self.namespace.get(handle).unwrap().type_of();
-                let converted_object = value.as_type(desired_type, self)?;
+                let converted_object = match self.namespace.get(handle).unwrap().type_of() {
+                    /*
+                     * We special-case FieldUnits here because we don't have the needed information to actually do
+                     * the write if we try and convert using `as_type`.
+                     */
+                    AmlType::FieldUnit => {
+                        let mut field = self.namespace.get(handle).unwrap().clone();
+                        field.write_field(value, self)?;
+                        field.read_field(self)?
+                    }
+                    typ => value.as_type(typ, self)?,
+                };
 
                 *self.namespace.get_mut(handle)? = converted_object;
                 Ok(self.namespace.get(handle)?.clone())
@@ -472,6 +484,98 @@ impl AmlContext {
             _ => unimplemented!(),
         }
     }
+
+    pub(crate) fn write_region(
+        &mut self,
+        region_handle: AmlHandle,
+        offset: u64,
+        length: u64,
+        value: u64,
+    ) -> Result<(), AmlError> {
+        use bit_field::BitField;
+        use core::convert::TryInto;
+        use value::RegionSpace;
+
+        let (region_space, region_base, region_length, parent_device) = {
+            if let AmlValue::OpRegion { region, offset, length, parent_device } =
+                self.namespace.get(region_handle)?
+            {
+                (region, offset, length, parent_device)
+            } else {
+                return Err(AmlError::FieldRegionIsNotOpRegion);
+            }
+        };
+
+        match region_space {
+            RegionSpace::SystemMemory => {
+                let address = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
+                match length {
+                    8 => Ok(self.handler.write_u8(address, value as u8)),
+                    16 => Ok(self.handler.write_u16(address, value as u16)),
+                    32 => Ok(self.handler.write_u32(address, value as u32)),
+                    64 => Ok(self.handler.write_u64(address, value)),
+                    _ => Err(AmlError::FieldInvalidAccessSize),
+                }
+            }
+
+            RegionSpace::SystemIo => {
+                let port = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
+                match length {
+                    8 => Ok(self.handler.write_io_u8(port, value as u8)),
+                    16 => Ok(self.handler.write_io_u16(port, value as u16)),
+                    32 => Ok(self.handler.write_io_u32(port, value as u32)),
+                    _ => Err(AmlError::FieldInvalidAccessSize),
+                }
+            }
+
+            RegionSpace::PciConfig => {
+                /*
+                 * First, we need to get some extra information out of objects in the parent object. Both
+                 * `_SEG` and `_BBN` seem optional, with defaults that line up with legacy PCI implementations
+                 * (e.g. systems with a single segment group and a single root, respectively).
+                 */
+                let parent_device = parent_device.as_ref().unwrap();
+                let seg = match self.namespace.search(&AmlName::from_str("_SEG").unwrap(), parent_device) {
+                    Ok((_, handle)) => self
+                        .namespace
+                        .get(handle)?
+                        .as_integer(self)?
+                        .try_into()
+                        .map_err(|_| AmlError::FieldInvalidAddress)?,
+                    Err(AmlError::ValueDoesNotExist(_)) => 0,
+                    Err(err) => return Err(err),
+                };
+                let bbn = match self.namespace.search(&AmlName::from_str("_BBN").unwrap(), parent_device) {
+                    Ok((_, handle)) => self
+                        .namespace
+                        .get(handle)?
+                        .as_integer(self)?
+                        .try_into()
+                        .map_err(|_| AmlError::FieldInvalidAddress)?,
+                    Err(AmlError::ValueDoesNotExist(_)) => 0,
+                    Err(err) => return Err(err),
+                };
+                let adr = {
+                    let (_, handle) = self.namespace.search(&AmlName::from_str("_ADR").unwrap(), parent_device)?;
+                    self.namespace.get(handle)?.as_integer(self)?
+                };
+
+                let device = adr.get_bits(16..24) as u8;
+                let function = adr.get_bits(0..8) as u8;
+                let offset = (region_base + offset).try_into().map_err(|_| AmlError::FieldInvalidAddress)?;
+
+                match length {
+                    8 => Ok(self.handler.write_pci_u8(seg, bbn, device, function, offset, value as u8)),
+                    16 => Ok(self.handler.write_pci_u16(seg, bbn, device, function, offset, value as u16)),
+                    32 => Ok(self.handler.write_pci_u32(seg, bbn, device, function, offset, value as u32)),
+                    _ => Err(AmlError::FieldInvalidAccessSize),
+                }
+            }
+
+            // TODO
+            _ => unimplemented!(),
+        }
+    }
 }
 
 pub trait Handler {
@@ -496,6 +600,10 @@ pub trait Handler {
     fn read_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u8;
     fn read_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u16;
     fn read_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u32;
+
+    fn write_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u8);
+    fn write_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u16);
+    fn write_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u32);
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]

+ 53 - 6
aml/src/value.rs

@@ -1,7 +1,6 @@
 use crate::{misc::ArgNum, AmlContext, AmlError, AmlHandle, AmlName};
 use alloc::{string::String, vec::Vec};
 use bit_field::BitField;
-use core::convert::TryInto;
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub enum RegionSpace {
@@ -26,7 +25,6 @@ pub enum FieldAccessType {
     DWord,
     QWord,
     Buffer,
-    Reserved,
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
@@ -276,17 +274,17 @@ impl AmlValue {
     ///     `Package` from: `Debug`
     ///     `String` from: `Integer`, `Buffer`, `Debug`
     pub fn as_type(&self, desired_type: AmlType, context: &AmlContext) -> Result<AmlValue, AmlError> {
-        // Cache the type of this object
-        let our_type = self.type_of();
-
         // If the value is already of the correct type, just return it as is
-        if our_type == desired_type {
+        if self.type_of() == desired_type {
             return Ok(self.clone());
         }
 
         // TODO: implement all of the rules
         match desired_type {
             AmlType::Integer => self.as_integer(context).map(|value| AmlValue::Integer(value)),
+            AmlType::FieldUnit => panic!(
+                "Can't implicitly convert to FieldUnit. This must be special-cased by the caller for now :("
+            ),
             _ => Err(AmlError::IncompatibleValueConversion),
         }
     }
@@ -306,6 +304,55 @@ impl AmlValue {
             Err(AmlError::IncompatibleValueConversion)
         }
     }
+
+    pub fn write_field(&mut self, value: AmlValue, context: &mut AmlContext) -> Result<(), AmlError> {
+        /*
+         * TODO:
+         * If we need to preserve the field's value, we'll need the contents of the field before we write it. To
+         * appease the borrow-checker, this is done before we destructure the field for now, but it would be more
+         * efficient if we could only do this if the field's update rule is Preserve.
+         */
+        let field_value = self.read_field(context)?.as_integer(context)?;
+
+        if let AmlValue::Field { region, flags, offset, length } = self {
+            let maximum_access_size = {
+                if let AmlValue::OpRegion { region, .. } = context.namespace.get(*region)? {
+                    match region {
+                        RegionSpace::SystemMemory => 64,
+                        RegionSpace::SystemIo | RegionSpace::PciConfig => 32,
+                        _ => unimplemented!(),
+                    }
+                } else {
+                    return Err(AmlError::FieldRegionIsNotOpRegion);
+                }
+            };
+            let minimum_access_size = match flags.access_type()? {
+                FieldAccessType::Any => 8,
+                FieldAccessType::Byte => 8,
+                FieldAccessType::Word => 16,
+                FieldAccessType::DWord => 32,
+                FieldAccessType::QWord => 64,
+                FieldAccessType::Buffer => 8, // TODO
+            };
+
+            /*
+             * Find the access size, as either the minimum access size allowed by the region, or the field length
+             * rounded up to the next power-of-2, whichever is larger.
+             */
+            let access_size = u64::max(minimum_access_size, length.next_power_of_two());
+
+            let mut value_to_write = match flags.field_update_rule()? {
+                FieldUpdateRule::Preserve => field_value,
+                FieldUpdateRule::WriteAsOnes => 0xffffffff_ffffffff,
+                FieldUpdateRule::WriteAsZeros => 0x0,
+            };
+            value_to_write.set_bits(0..(*length as usize), value.as_integer(context)?);
+
+            context.write_region(*region, *offset, access_size, value_to_write)
+        } else {
+            Err(AmlError::IncompatibleValueConversion)
+        }
+    }
 }
 
 /// A control method can take up to 7 arguments, each of which can be an `AmlValue`.

+ 9 - 0
aml_tester/src/main.rs

@@ -196,4 +196,13 @@ impl aml::Handler for Handler {
     fn read_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16) -> u32 {
         unimplemented!()
     }
+    fn write_pci_u8(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u8) {
+        unimplemented!()
+    }
+    fn write_pci_u16(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u16) {
+        unimplemented!()
+    }
+    fn write_pci_u32(&self, segment: u16, bus: u8, device: u8, function: u8, offset: u16, value: u32) {
+        unimplemented!()
+    }
 }