Implement Xlat::get_access_rights

Implement function for querying memory access rights of a given virtual
address.

Signed-off-by: Imre Kis <imre.kis@arm.com>
Change-Id: I4c43a743a4fd10872f3a15ca1c8d634175706b16
diff --git a/src/lib.rs b/src/lib.rs
index 496681c..f1f1234 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -125,6 +125,37 @@
     }
 }
 
+impl From<Attributes> for MemoryAccessRights {
+    fn from(value: Attributes) -> Self {
+        let mut result = Self::empty();
+
+        result |= match value.data_access_permissions {
+            DataAccessPermissions::ReadOnly_None => Self::R,
+            DataAccessPermissions::ReadWrite_None => Self::RW,
+            DataAccessPermissions::ReadOnly_ReadOnly => Self::R | Self::USER,
+            DataAccessPermissions::ReadWrite_ReadWrite => Self::RW | Self::USER,
+        };
+
+        if value.mem_attr_index == MemoryAttributesIndex::Device_nGnRnE {
+            result |= Self::DEVICE;
+        }
+
+        if !value.uxn {
+            result |= Self::X;
+        }
+
+        if !value.not_global {
+            result |= Self::GLOBAL;
+        }
+
+        if value.non_secure {
+            result |= Self::NS;
+        }
+
+        result
+    }
+}
+
 /// Virtual Address range, selects x in `TTBRx_EL*`
 #[derive(Debug, Clone, Copy)]
 pub enum RegimeVaRange {
@@ -414,6 +445,39 @@
         Ok(())
     }
 
+    /// Query the memory access rights of virtual address
+    /// # Arguments
+    /// * va : Virtual address, alignment to granule size is not required
+    /// # Return value
+    /// Memory access rights of the virtual address and the minimal length of a continuous memory
+    /// area after the virtual address that is guaranteed to have the same access rights. The
+    /// following area after the returned length might or might not have the same access rights.
+    pub fn get_access_rights(
+        &self,
+        va: VirtualAddress,
+    ) -> Result<(MemoryAccessRights, usize), XlatError> {
+        let containing_region = self
+            .find_containing_region(va, 1)
+            .ok_or(XlatError::VaNotFound(va))?;
+
+        if !containing_region.used() {
+            return Err(XlatError::VaNotFound(va));
+        }
+
+        let (descriptor, level) = self.get_descriptor(va.remove_upper_bits::<VA_BITS>());
+
+        match descriptor.get_descriptor_type(level) {
+            DescriptorType::Block => {
+                let attributes = descriptor.get_block_attributes(level);
+                let block_length = self.granule.block_size_at_level(level);
+                let length = block_length - va.mask_bits(block_length - 1).0;
+
+                Ok((MemoryAccessRights::from(attributes), length))
+            }
+            _ => Err(XlatError::VaNotFound(va)),
+        }
+    }
+
     /// Activate memory mapping represented by the object
     ///
     /// # Safety
@@ -934,41 +998,42 @@
         }
     }
 
-    fn get_descriptor(&mut self, va: VirtualAddress, block_size: usize) -> &mut Descriptor {
+    /// Find a block or an invalid descriptor which describes the virtual address' mapping.
+    /// # Arguments
+    /// * va : Virtual address, alignment to granule size is not required
+    /// # Return value
+    /// Reference to the descriptor and level of the descriptor in the translation table
+    fn get_descriptor(&self, va: VirtualAddress) -> (&Descriptor, isize) {
         Self::walk_descriptors(
             va,
-            block_size,
             self.granule.initial_lookup_level(),
-            unsafe { self.base_table.get_as_mut_slice::<K, Descriptor>() },
+            unsafe { self.base_table.get_as_slice::<K, Descriptor>() },
             self.granule,
         )
     }
 
+    /// Walk translation table until finding a block or invalid descriptor that contains the VA.
+    /// # Arguments
+    /// * va : Virtual address, alignment to granule size is not required
+    /// * table: Translation table on the given level
+    /// * granule: Translation granule
+    /// # Return value
+    /// Reference to the descriptor and level of the descriptor in the translation table
     fn walk_descriptors(
         va: VirtualAddress,
-        block_size: usize,
         level: isize,
-        table: &mut [Descriptor],
+        table: &[Descriptor],
         granule: TranslationGranule<VA_BITS>,
-    ) -> &mut Descriptor {
+    ) -> (&Descriptor, isize) {
         // Get descriptor of the current level
-        let descriptor = &mut table[va.get_level_index(granule, level)];
-
-        if granule.block_size_at_level(level) == block_size {
-            return descriptor;
-        }
+        let descriptor = &table[va.get_level_index(granule, level)];
 
         // Need to iterate forward
         match descriptor.get_descriptor_type(level) {
-            DescriptorType::Invalid => {
-                panic!("Invalid descriptor");
-            }
-            DescriptorType::Block => {
-                panic!("Cannot split existing block descriptor to table");
-            }
+            DescriptorType::Invalid | DescriptorType::Block => (descriptor, level),
             DescriptorType::Table => {
                 let next_level_table = unsafe {
-                    Self::get_table_from_pa_mut(
+                    Self::get_table_from_pa(
                         descriptor.get_next_level_table(level),
                         granule,
                         level + 1,
@@ -977,7 +1042,6 @@
 
                 Self::walk_descriptors(
                     va.mask_for_level(granule, level),
-                    block_size,
                     level + 1,
                     next_level_table,
                     granule,