parent
							
								
									5610d0bdb0
								
							
						
					
					
						commit
						ef213d60bb
					
				| @ -0,0 +1,68 @@ | ||||
| [[package]] | ||||
| name = "aarch64" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
|  "ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bit_field" | ||||
| version = "0.9.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "bitflags" | ||||
| version = "1.0.4" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "cortex-a" | ||||
| version = "2.2.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "os_bootinfo" | ||||
| version = "0.2.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "register" | ||||
| version = "0.2.1" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| dependencies = [ | ||||
|  "tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "tock-registers" | ||||
| version = "0.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "usize_conversions" | ||||
| version = "0.2.0" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 
 | ||||
| [[package]] | ||||
| name = "ux" | ||||
| version = "0.1.2" | ||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | ||||
| 
 | ||||
| [metadata] | ||||
| "checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" | ||||
| "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" | ||||
| "checksum cortex-a 2.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2b187d0d728b4a99ba1d79f9671b976bcdd71a8a2c719585218fd2dc14a4d08c" | ||||
| "checksum os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66481dbeb5e773e7bd85b63cd6042c30786f834338288c5ec4f3742673db360a" | ||||
| "checksum register 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e10f31b6d2299e5620986ad9fcdd66463e125ad72af4f403f9aedf7592d5ccdb" | ||||
| "checksum tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a385d94f3f62e60445a0adb9ff8d9621faa272234530d4c0f848ec98f88e316" | ||||
| "checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" | ||||
| "checksum ux 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53d8df5dd8d07fedccd202de1887d94481fadaea3db70479f459e8163a1fab41" | ||||
| @ -0,0 +1,16 @@ | ||||
| [package] | ||||
| name = "aarch64" | ||||
| version = "0.1.0" | ||||
| authors = ["koumingyang <1761674434@qq.com>"] | ||||
| 
 | ||||
| [dependencies] | ||||
| register = "0.2.0" | ||||
| bit_field = "0.9.0" | ||||
| bitflags = "1.0.1" | ||||
| usize_conversions = "0.2.0" | ||||
| os_bootinfo = "0.2.0" | ||||
| bare-metal = "0.2.0" | ||||
| 
 | ||||
| [dependencies.ux] | ||||
| default-features = false | ||||
| version = "0.1.0" | ||||
| @ -0,0 +1,418 @@ | ||||
| use core::convert::{Into, TryInto}; | ||||
| use core::fmt; | ||||
| use core::ops::{Add, AddAssign, Sub, SubAssign}; | ||||
| 
 | ||||
| use bit_field::BitField; | ||||
| use usize_conversions::FromUsize; | ||||
| use ux::*; | ||||
| 
 | ||||
| #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||||
| #[repr(transparent)] | ||||
| pub struct VirtAddr(u64); | ||||
| 
 | ||||
| #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||||
| #[repr(transparent)] | ||||
| pub struct PhysAddr(u64); | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct VirtAddrNotValid(u64); | ||||
| 
 | ||||
| impl VirtAddr { | ||||
|     /// Creates a new canonical virtual address.
 | ||||
|     ///
 | ||||
|     /// This function performs sign extension of bit 47 to make the address canonical. Panics
 | ||||
|     /// if the bits in the range 48 to 64 contain data (i.e. are not null and no sign extension).
 | ||||
|     pub fn new(addr: u64) -> VirtAddr { | ||||
|         Self::try_new(addr).expect( | ||||
|             "invalid virtual address", | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     /// Tries to create a new canonical virtual address.
 | ||||
|     /// in aarch64, valid virtual address starts with 0x0000 or 0xffff.
 | ||||
|     pub fn try_new(addr: u64) -> Result<VirtAddr, VirtAddrNotValid> { | ||||
|         match addr.get_bits(48..64) { | ||||
|             0 | 0xffff => Ok(VirtAddr(addr)),      // address is canonical
 | ||||
|             other => Err(VirtAddrNotValid(other)), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn new_unchecked(addr: u64) -> VirtAddr { | ||||
|         VirtAddr(addr) | ||||
|     } | ||||
| 
 | ||||
|     /// Creates a virtual address that points to `0`.
 | ||||
|     pub const fn zero() -> VirtAddr { | ||||
|         VirtAddr(0) | ||||
|     } | ||||
| 
 | ||||
|     /// Converts the address to an `u64`.
 | ||||
|     pub fn as_u64(self) -> u64 { | ||||
|         self.0 | ||||
|     } | ||||
| 
 | ||||
|     /// Creates a virtual address from the given pointer
 | ||||
|     pub fn from_ptr<T>(ptr: *const T) -> Self { | ||||
|         use usize_conversions::FromUsize; | ||||
| 
 | ||||
|         Self::new(u64::from_usize(ptr as usize)) | ||||
|     } | ||||
| 
 | ||||
|     /// Converts the address to a raw pointer.
 | ||||
|     #[cfg(target_pointer_width = "64")] | ||||
|     pub fn as_ptr<T>(self) -> *const T { | ||||
|         use usize_conversions::usize_from; | ||||
| 
 | ||||
|         usize_from(self.as_u64()) as *const T | ||||
|     } | ||||
| 
 | ||||
|     /// Converts the address to a mutable raw pointer.
 | ||||
|     #[cfg(target_pointer_width = "64")] | ||||
|     pub fn as_mut_ptr<T>(self) -> *mut T { | ||||
|         self.as_ptr::<T>() as *mut T | ||||
|     } | ||||
| 
 | ||||
|     /// Aligns the virtual address upwards to the given alignment.
 | ||||
|     ///
 | ||||
|     /// See the `align_up` function for more information.
 | ||||
|     pub fn align_up<U>(self, align: U) -> Self | ||||
|     where | ||||
|         U: Into<u64>, | ||||
|     { | ||||
|         VirtAddr(align_up(self.0, align.into())) | ||||
|     } | ||||
| 
 | ||||
|     /// Aligns the virtual address downwards to the given alignment.
 | ||||
|     ///
 | ||||
|     /// See the `align_down` function for more information.
 | ||||
|     pub fn align_down<U>(self, align: U) -> Self | ||||
|     where | ||||
|         U: Into<u64>, | ||||
|     { | ||||
|         VirtAddr(align_down(self.0, align.into())) | ||||
|     } | ||||
| 
 | ||||
|     /// Checks whether the virtual address has the demanded alignment.
 | ||||
|     pub fn is_aligned<U>(self, align: U) -> bool | ||||
|     where | ||||
|         U: Into<u64>, | ||||
|     { | ||||
|         self.align_down(align) == self | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the 12-bit page offset of this virtual address.
 | ||||
|     pub fn page_offset(&self) -> u12 { | ||||
|         u12::new((self.0 & 0xfff).try_into().unwrap()) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the 9-bit level 1 page table index.
 | ||||
|     pub fn p1_index(&self) -> u9 { | ||||
|         u9::new(((self.0 >> 12) & 0o777).try_into().unwrap()) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the 9-bit level 2 page table index.
 | ||||
|     pub fn p2_index(&self) -> u9 { | ||||
|         u9::new(((self.0 >> 12 >> 9) & 0o777).try_into().unwrap()) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the 9-bit level 3 page table index.
 | ||||
|     pub fn p3_index(&self) -> u9 { | ||||
|         u9::new(((self.0 >> 12 >> 9 >> 9) & 0o777).try_into().unwrap()) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the 9-bit level 4 page table index.
 | ||||
|     pub fn p4_index(&self) -> u9 { | ||||
|         u9::new(((self.0 >> 12 >> 9 >> 9 >> 9) & 0o777).try_into().unwrap()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for VirtAddr { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         write!(f, "VirtAddr({:#x})", self.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Add<u64> for VirtAddr { | ||||
|     type Output = Self; | ||||
|     fn add(self, rhs: u64) -> Self::Output { | ||||
|         VirtAddr::new(self.0 + rhs) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl AddAssign<u64> for VirtAddr { | ||||
|     fn add_assign(&mut self, rhs: u64) { | ||||
|         *self = *self + rhs; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Add<usize> for VirtAddr | ||||
| where | ||||
|     u64: FromUsize, | ||||
| { | ||||
|     type Output = Self; | ||||
|     fn add(self, rhs: usize) -> Self::Output { | ||||
|         self + u64::from_usize(rhs) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl AddAssign<usize> for VirtAddr | ||||
| where | ||||
|     u64: FromUsize, | ||||
| { | ||||
|     fn add_assign(&mut self, rhs: usize) { | ||||
|         self.add_assign(u64::from_usize(rhs)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Sub<u64> for VirtAddr { | ||||
|     type Output = Self; | ||||
|     fn sub(self, rhs: u64) -> Self::Output { | ||||
|         VirtAddr::new(self.0.checked_sub(rhs).unwrap()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SubAssign<u64> for VirtAddr { | ||||
|     fn sub_assign(&mut self, rhs: u64) { | ||||
|         *self = *self - rhs; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Sub<usize> for VirtAddr | ||||
| where | ||||
|     u64: FromUsize, | ||||
| { | ||||
|     type Output = Self; | ||||
|     fn sub(self, rhs: usize) -> Self::Output { | ||||
|         self - u64::from_usize(rhs) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SubAssign<usize> for VirtAddr | ||||
| where | ||||
|     u64: FromUsize, | ||||
| { | ||||
|     fn sub_assign(&mut self, rhs: usize) { | ||||
|         self.sub_assign(u64::from_usize(rhs)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Sub<VirtAddr> for VirtAddr { | ||||
|     type Output = u64; | ||||
|     fn sub(self, rhs: VirtAddr) -> Self::Output { | ||||
|         self.as_u64().checked_sub(rhs.as_u64()).unwrap() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// A passed `u64` was not a valid physical address.
 | ||||
| ///
 | ||||
| /// This means that bits 52 to 64 are not were not all null.
 | ||||
| #[derive(Debug)] | ||||
| pub struct PhysAddrNotValid(u64); | ||||
| 
 | ||||
| impl PhysAddr { | ||||
|     /// Creates a new physical address.
 | ||||
|     ///
 | ||||
|     /// Panics if a bit in the range 52 to 64 is set.
 | ||||
|     pub fn new(addr: u64) -> PhysAddr { | ||||
|         assert_eq!( | ||||
|             addr.get_bits(52..64), | ||||
|             0, | ||||
|             "physical addresses must not have any bits in the range 52 to 64 set" | ||||
|         ); | ||||
|         PhysAddr(addr) | ||||
|     } | ||||
| 
 | ||||
|     /// Tries to create a new physical address.
 | ||||
|     ///
 | ||||
|     /// Fails if any bits in the range 52 to 64 are set.
 | ||||
|     pub fn try_new(addr: u64) -> Result<PhysAddr, PhysAddrNotValid> { | ||||
|         match addr.get_bits(52..64) { | ||||
|             0 => Ok(PhysAddr(addr)), // address is valid
 | ||||
|             other => Err(PhysAddrNotValid(other)), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Converts the address to an `u64`.
 | ||||
|     pub fn as_u64(self) -> u64 { | ||||
|         self.0 | ||||
|     } | ||||
| 
 | ||||
|     /// Convenience method for checking if a physical address is null.
 | ||||
|     pub fn is_null(&self) -> bool { | ||||
|         self.0 == 0 | ||||
|     } | ||||
| 
 | ||||
|     /// Aligns the physical address upwards to the given alignment.
 | ||||
|     ///
 | ||||
|     /// See the `align_up` function for more information.
 | ||||
|     pub fn align_up<U>(self, align: U) -> Self | ||||
|     where | ||||
|         U: Into<u64>, | ||||
|     { | ||||
|         PhysAddr(align_up(self.0, align.into())) | ||||
|     } | ||||
| 
 | ||||
|     /// Aligns the physical address downwards to the given alignment.
 | ||||
|     ///
 | ||||
|     /// See the `align_down` function for more information.
 | ||||
|     pub fn align_down<U>(self, align: U) -> Self | ||||
|     where | ||||
|         U: Into<u64>, | ||||
|     { | ||||
|         PhysAddr(align_down(self.0, align.into())) | ||||
|     } | ||||
| 
 | ||||
|     /// Checks whether the physical address has the demanded alignment.
 | ||||
|     pub fn is_aligned<U>(self, align: U) -> bool | ||||
|     where | ||||
|         U: Into<u64>, | ||||
|     { | ||||
|         self.align_down(align) == self | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for PhysAddr { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         write!(f, "PhysAddr({:#x})", self.0) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Binary for PhysAddr { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         self.0.fmt(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::LowerHex for PhysAddr { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         self.0.fmt(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Octal for PhysAddr { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         self.0.fmt(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::UpperHex for PhysAddr { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         self.0.fmt(f) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Add<u64> for PhysAddr { | ||||
|     type Output = Self; | ||||
|     fn add(self, rhs: u64) -> Self::Output { | ||||
|         PhysAddr::new(self.0 + rhs) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl AddAssign<u64> for PhysAddr { | ||||
|     fn add_assign(&mut self, rhs: u64) { | ||||
|         *self = *self + rhs; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Add<usize> for PhysAddr | ||||
| where | ||||
|     u64: FromUsize, | ||||
| { | ||||
|     type Output = Self; | ||||
|     fn add(self, rhs: usize) -> Self::Output { | ||||
|         self + u64::from_usize(rhs) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl AddAssign<usize> for PhysAddr | ||||
| where | ||||
|     u64: FromUsize, | ||||
| { | ||||
|     fn add_assign(&mut self, rhs: usize) { | ||||
|         self.add_assign(u64::from_usize(rhs)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Sub<u64> for PhysAddr { | ||||
|     type Output = Self; | ||||
|     fn sub(self, rhs: u64) -> Self::Output { | ||||
|         PhysAddr::new(self.0.checked_sub(rhs).unwrap()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SubAssign<u64> for PhysAddr { | ||||
|     fn sub_assign(&mut self, rhs: u64) { | ||||
|         *self = *self - rhs; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Sub<usize> for PhysAddr | ||||
| where | ||||
|     u64: FromUsize, | ||||
| { | ||||
|     type Output = Self; | ||||
|     fn sub(self, rhs: usize) -> Self::Output { | ||||
|         self - u64::from_usize(rhs) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SubAssign<usize> for PhysAddr | ||||
| where | ||||
|     u64: FromUsize, | ||||
| { | ||||
|     fn sub_assign(&mut self, rhs: usize) { | ||||
|         self.sub_assign(u64::from_usize(rhs)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Sub<PhysAddr> for PhysAddr { | ||||
|     type Output = u64; | ||||
|     fn sub(self, rhs: PhysAddr) -> Self::Output { | ||||
|         self.as_u64().checked_sub(rhs.as_u64()).unwrap() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Align address downwards.
 | ||||
| ///
 | ||||
| /// Returns the greatest x with alignment `align` so that x <= addr. The alignment must be
 | ||||
| ///  a power of 2.
 | ||||
| pub fn align_down(addr: u64, align: u64) -> u64 { | ||||
|     assert!(align.is_power_of_two(), "`align` must be a power of two"); | ||||
|     addr & !(align - 1) | ||||
| } | ||||
| 
 | ||||
| /// Align address upwards.
 | ||||
| ///
 | ||||
| /// Returns the smallest x with alignment `align` so that x >= addr. The alignment must be
 | ||||
| /// a power of 2.
 | ||||
| pub fn align_up(addr: u64, align: u64) -> u64 { | ||||
|     assert!(align.is_power_of_two(), "`align` must be a power of two"); | ||||
|     let align_mask = align - 1; | ||||
|     if addr & align_mask == 0 { | ||||
|         addr // already aligned
 | ||||
|     } else { | ||||
|         (addr | align_mask) + 1 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     pub fn test_align_up() { | ||||
|         // align 1
 | ||||
|         assert_eq!(align_up(0, 1), 0); | ||||
|         assert_eq!(align_up(1234, 1), 1234); | ||||
|         assert_eq!(align_up(0xffffffffffffffff, 1), 0xffffffffffffffff); | ||||
|         // align 2
 | ||||
|         assert_eq!(align_up(0, 2), 0); | ||||
|         assert_eq!(align_up(1233, 2), 1234); | ||||
|         assert_eq!(align_up(0xfffffffffffffffe, 2), 0xfffffffffffffffe); | ||||
|         // address 0
 | ||||
|         assert_eq!(align_up(0, 128), 0); | ||||
|         assert_eq!(align_up(0, 1), 0); | ||||
|         assert_eq!(align_up(0, 2), 0); | ||||
|         assert_eq!(align_up(0, 0x8000000000000000), 0); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,162 @@ | ||||
| use paging::PhysFrame; | ||||
| use addr::PhysAddr; | ||||
| use regs::*; | ||||
| 
 | ||||
| #[inline(always)] | ||||
| pub fn tlb_invalidate() { | ||||
|     unsafe{ | ||||
|         asm!("dsb ishst
 | ||||
|               tlbi vmalle1is | ||||
|               dsb ish | ||||
|               tlbi vmalle1is | ||||
|               isb");
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Returns the current stack pointer.
 | ||||
| #[inline(always)] | ||||
| pub fn sp() -> *const u8 { | ||||
|     let ptr: usize; | ||||
|     unsafe { | ||||
|         asm!("mov $0, sp" : "=r"(ptr)); | ||||
|     } | ||||
| 
 | ||||
|     ptr as *const u8 | ||||
| } | ||||
| 
 | ||||
| #[inline(always)] | ||||
| pub unsafe fn get_pc() -> usize { | ||||
|     let pc: usize; | ||||
|     asm!("ADR $0, ." : "=r"(pc)); | ||||
|     pc | ||||
| } | ||||
| 
 | ||||
| /// Returns the current exception level.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| /// This function should only be called when EL is >= 1.
 | ||||
| #[inline(always)] | ||||
| pub unsafe fn current_el() -> u8 { | ||||
|     let el_reg: u64; | ||||
|     asm!("mrs $0, CurrentEL" : "=r"(el_reg)); | ||||
|     ((el_reg & 0b1100) >> 2) as u8 | ||||
| } | ||||
| 
 | ||||
| #[inline(always)] | ||||
| pub unsafe fn get_far() -> usize { | ||||
|     let far: usize; | ||||
|     asm!("mrs $0, far_el1" : "=r"(far)); | ||||
|     far | ||||
| } | ||||
| 
 | ||||
| #[inline(always)] | ||||
| pub unsafe fn get_ttbr0() -> usize { | ||||
|     let ttbr0: usize; | ||||
|     asm!("mrs $0, ttbr0_el1" : "=r"(ttbr0)); | ||||
|     ttbr0 | ||||
| } | ||||
| 
 | ||||
| #[inline(always)] | ||||
| pub unsafe fn get_ttbr1() -> usize { | ||||
|     let ttbr0: usize; | ||||
|     asm!("mrs $0, ttbr1_el1" : "=r"(ttbr0)); | ||||
|     ttbr0 | ||||
| } | ||||
| 
 | ||||
| /// Returns the SPSel value.
 | ||||
| #[inline(always)] | ||||
| pub fn sp_sel() -> u8 { | ||||
|     let ptr: u32; | ||||
|     unsafe { | ||||
|         asm!("mrs $0, SPSel" : "=r"(ptr)); | ||||
|     } | ||||
| 
 | ||||
|     (ptr & 1) as u8 | ||||
| } | ||||
| 
 | ||||
| /// Returns the core currently executing.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// This function should only be called when EL is >= 1.
 | ||||
| pub unsafe fn affinity() -> usize { | ||||
|     let x: usize; | ||||
|     asm!("mrs     $0, mpidr_el1
 | ||||
|           and     $0, $0, #3" | ||||
|           : "=r"(x)); | ||||
| 
 | ||||
|     x | ||||
| } | ||||
| 
 | ||||
| pub fn wfi() { | ||||
|     unsafe { | ||||
|         asm!("wfi" :::: "volatile"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /// The classic no-op
 | ||||
| #[inline] | ||||
| pub fn nop() { | ||||
|     match () { | ||||
|         #[cfg(target_arch = "aarch64")] | ||||
|         () => unsafe { asm!("nop" :::: "volatile") }, | ||||
| 
 | ||||
|         #[cfg(not(target_arch = "aarch64"))] | ||||
|         () => unimplemented!(), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Wait For Event
 | ||||
| #[inline] | ||||
| pub fn wfe() { | ||||
|     match () { | ||||
|         #[cfg(target_arch = "aarch64")] | ||||
|         () => unsafe { asm!("wfe" :::: "volatile") }, | ||||
| 
 | ||||
|         #[cfg(not(target_arch = "aarch64"))] | ||||
|         () => unimplemented!(), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Exception return
 | ||||
| ///
 | ||||
| /// Will jump to wherever the corresponding link register points to, and
 | ||||
| /// therefore never return.
 | ||||
| #[inline] | ||||
| pub fn eret() -> ! { | ||||
|     use core; | ||||
| 
 | ||||
|     match () { | ||||
|         #[cfg(target_arch = "aarch64")] | ||||
|         () => unsafe { | ||||
|             asm!("eret" :::: "volatile"); | ||||
|             core::intrinsics::unreachable() | ||||
|         }, | ||||
| 
 | ||||
|         #[cfg(not(target_arch = "aarch64"))] | ||||
|         () => unimplemented!(), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bitflags! { | ||||
|     /// Controls cache settings for the level 4 page table.
 | ||||
|     pub struct ttbr0_el1_Flags: u64 { | ||||
|         
 | ||||
|         const COMMON_NOT_PRIVATE = 1 << 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn ttbr0_el1_read() -> (PhysFrame, ttbr0_el1_Flags) { | ||||
|     let value = TTBR0_EL1.get(); | ||||
|     let flags = ttbr0_el1_Flags::from_bits_truncate(value); | ||||
|     let addr = PhysAddr::new(value & 0x_000f_ffff_ffff_f000); | ||||
|     let frame = PhysFrame::containing_address(addr); | ||||
|     (frame, flags) | ||||
| } | ||||
| 
 | ||||
| pub fn ttbr0_el1_write(frame: PhysFrame) { | ||||
|     let addr = frame.start_address(); | ||||
|     let value = addr.as_u64(); | ||||
|     TTBR0_EL1.set_baddr(value); | ||||
| } | ||||
| @ -0,0 +1,87 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| // Borrow implementations from the pending upstream ACLE implementation until it
 | ||||
| // is merged. Afterwards, we'll probably just reexport them, hoping that the API
 | ||||
| // doesn't change.
 | ||||
| //
 | ||||
| // https://github.com/rust-lang-nursery/stdsimd/pull/557
 | ||||
| 
 | ||||
| mod sealed { | ||||
|     pub trait Dmb { | ||||
|         unsafe fn __dmb(&self); | ||||
|     } | ||||
| 
 | ||||
|     pub trait Dsb { | ||||
|         unsafe fn __dsb(&self); | ||||
|     } | ||||
| 
 | ||||
|     pub trait Isb { | ||||
|         unsafe fn __isb(&self); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| macro_rules! dmb_dsb { | ||||
|     ($A:ident) => { | ||||
|         impl sealed::Dmb for $A { | ||||
|             #[inline(always)] | ||||
|             unsafe fn __dmb(&self) { | ||||
|                 asm!(concat!("DMB ", stringify!($A)) : : : "memory" : "volatile") | ||||
|             } | ||||
|         } | ||||
|         impl sealed::Dsb for $A { | ||||
|             #[inline(always)] | ||||
|             unsafe fn __dsb(&self) { | ||||
|                 asm!(concat!("DSB ", stringify!($A)) : : : "memory" : "volatile") | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| pub struct SY; | ||||
| 
 | ||||
| dmb_dsb!(SY); | ||||
| 
 | ||||
| impl sealed::Isb for SY { | ||||
|     #[inline(always)] | ||||
|     unsafe fn __isb(&self) { | ||||
|         asm!("ISB SY" : : : "memory" : "volatile") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[inline(always)] | ||||
| pub unsafe fn dmb<A>(arg: A) | ||||
| where | ||||
|     A: sealed::Dmb, | ||||
| { | ||||
|     arg.__dmb() | ||||
| } | ||||
| 
 | ||||
| #[inline(always)] | ||||
| pub unsafe fn dsb<A>(arg: A) | ||||
| where | ||||
|     A: sealed::Dsb, | ||||
| { | ||||
|     arg.__dsb() | ||||
| } | ||||
| 
 | ||||
| #[inline(always)] | ||||
| pub unsafe fn isb<A>(arg: A) | ||||
| where | ||||
|     A: sealed::Isb, | ||||
| { | ||||
|     arg.__isb() | ||||
| } | ||||
| @ -0,0 +1,15 @@ | ||||
| //! Traits for abstracting away frame allocation and deallocation.
 | ||||
| 
 | ||||
| use paging::{PageSize, PhysFrame}; | ||||
| 
 | ||||
| /// A trait for types that can allocate a frame of memory.
 | ||||
| pub trait FrameAllocator<S: PageSize> { | ||||
|     /// Allocate a frame of the appropriate size and return it if possible.
 | ||||
|     fn alloc(&mut self) -> Option<PhysFrame<S>>; | ||||
| } | ||||
| 
 | ||||
| /// A trait for types that can deallocate a frame of memory.
 | ||||
| pub trait FrameDeallocator<S: PageSize> { | ||||
|     /// Deallocate the given frame of memory.
 | ||||
|     fn dealloc(&mut self, frame: PhysFrame<S>); | ||||
| } | ||||
| @ -0,0 +1,528 @@ | ||||
| //! Abstractions for page tables and other paging related structures.
 | ||||
| //!
 | ||||
| //! Page tables translate virtual memory “pages” to physical memory “frames”.
 | ||||
| 
 | ||||
| pub use self::frame_alloc::*; | ||||
| pub use self::page_table::*; | ||||
| #[cfg(target_arch = "aarch64")] | ||||
| pub use self::recursive::*; | ||||
| 
 | ||||
| use core::fmt; | ||||
| use core::marker::PhantomData; | ||||
| use core::ops::{Add, AddAssign, Sub, SubAssign}; | ||||
| use os_bootinfo; | ||||
| use ux::*; | ||||
| use addr::{PhysAddr, VirtAddr}; | ||||
| 
 | ||||
| mod frame_alloc; | ||||
| mod page_table; | ||||
| mod recursive; | ||||
| 
 | ||||
| /// Trait for abstracting over the three possible page sizes on x86_64, 4KiB, 2MiB, 1GiB.
 | ||||
| pub trait PageSize: Copy + Eq + PartialOrd + Ord { | ||||
|     /// The page size in bytes.
 | ||||
|     const SIZE: u64; | ||||
| 
 | ||||
|     /// A string representation of the page size for debug output.
 | ||||
|     const SIZE_AS_DEBUG_STR: &'static str; | ||||
| } | ||||
| 
 | ||||
| /// This trait is implemented for 4KiB and 2MiB pages, but not for 1GiB pages.
 | ||||
| pub trait NotGiantPageSize: PageSize {} | ||||
| 
 | ||||
| /// A standard 4KiB page.
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||||
| pub enum Size4KiB {} | ||||
| 
 | ||||
| /// A “huge” 2MiB page.
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||||
| pub enum Size2MiB {} | ||||
| 
 | ||||
| /// A “giant” 1GiB page.
 | ||||
| ///
 | ||||
| /// (Only available on newer x86_64 CPUs.)
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||||
| pub enum Size1GiB {} | ||||
| 
 | ||||
| impl PageSize for Size4KiB { | ||||
|     const SIZE: u64 = 4096; | ||||
|     const SIZE_AS_DEBUG_STR: &'static str = "4KiB"; | ||||
| } | ||||
| 
 | ||||
| impl NotGiantPageSize for Size4KiB {} | ||||
| 
 | ||||
| impl PageSize for Size2MiB { | ||||
|     const SIZE: u64 = Size4KiB::SIZE * 512; | ||||
|     const SIZE_AS_DEBUG_STR: &'static str = "2MiB"; | ||||
| } | ||||
| 
 | ||||
| impl NotGiantPageSize for Size2MiB {} | ||||
| 
 | ||||
| impl PageSize for Size1GiB { | ||||
|     const SIZE: u64 = Size2MiB::SIZE * 512; | ||||
|     const SIZE_AS_DEBUG_STR: &'static str = "1GiB"; | ||||
| } | ||||
| 
 | ||||
| /// A virtual memory page.
 | ||||
| #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||||
| #[repr(C)] | ||||
| pub struct Page<S: PageSize = Size4KiB> { | ||||
|     start_address: VirtAddr, | ||||
|     size: PhantomData<S>, | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> Page<S> { | ||||
|     /// Returns the page that starts at the given virtual address.
 | ||||
|     ///
 | ||||
|     /// Returns an error if the address is not correctly aligned (i.e. is not a valid page start).
 | ||||
|     pub fn from_start_address(address: VirtAddr) -> Result<Self, ()> { | ||||
|         if !address.is_aligned(S::SIZE) { | ||||
|             return Err(()); | ||||
|         } | ||||
|         Ok(Page::containing_address(address)) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the page that contains the given virtual address.
 | ||||
|     pub fn containing_address(address: VirtAddr) -> Self { | ||||
|         Page { | ||||
|             start_address: address.align_down(S::SIZE), | ||||
|             size: PhantomData, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the start address of the page.
 | ||||
|     pub fn start_address(&self) -> VirtAddr { | ||||
|         self.start_address | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the size the page (4KB, 2MB or 1GB).
 | ||||
|     pub const fn size(&self) -> u64 { | ||||
|         S::SIZE | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the level 4 page table index of this page.
 | ||||
|     pub fn p4_index(&self) -> u9 { | ||||
|         self.start_address().p4_index() | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the level 3 page table index of this page.
 | ||||
|     pub fn p3_index(&self) -> u9 { | ||||
|         self.start_address().p3_index() | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a range of pages, exclusive `end`.
 | ||||
|     pub fn range(start: Self, end: Self) -> PageRange<S> { | ||||
|         PageRange { start, end } | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a range of pages, inclusive `end`.
 | ||||
|     pub fn range_inclusive(start: Self, end: Self) -> PageRangeInclusive<S> { | ||||
|         PageRangeInclusive { start, end } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: NotGiantPageSize> Page<S> { | ||||
|     /// Returns the level 2 page table index of this page.
 | ||||
|     pub fn p2_index(&self) -> u9 { | ||||
|         self.start_address().p2_index() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Page<Size1GiB> { | ||||
|     /// Returns the 1GiB memory page with the specified page table indices.
 | ||||
|     pub fn from_page_table_indices_1gib(p4_index: u9, p3_index: u9) -> Self { | ||||
|         use bit_field::BitField; | ||||
| 
 | ||||
|         let mut addr = 0; | ||||
|         addr.set_bits(39..48, u64::from(p4_index)); | ||||
|         addr.set_bits(30..39, u64::from(p3_index)); | ||||
|         Page::containing_address(VirtAddr::new(addr)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Page<Size2MiB> { | ||||
|     /// Returns the 2MiB memory page with the specified page table indices.
 | ||||
|     pub fn from_page_table_indices_2mib(p4_index: u9, p3_index: u9, p2_index: u9) -> Self { | ||||
|         use bit_field::BitField; | ||||
| 
 | ||||
|         let mut addr = 0; | ||||
|         addr.set_bits(39..48, u64::from(p4_index)); | ||||
|         addr.set_bits(30..39, u64::from(p3_index)); | ||||
|         addr.set_bits(21..30, u64::from(p2_index)); | ||||
|         Page::containing_address(VirtAddr::new(addr)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Page<Size4KiB> { | ||||
|     /// Returns the 4KiB memory page with the specified page table indices.
 | ||||
|     pub fn from_page_table_indices(p4_index: u9, p3_index: u9, p2_index: u9, p1_index: u9) -> Self { | ||||
|         use bit_field::BitField; | ||||
| 
 | ||||
|         let mut addr = 0; | ||||
|         addr.set_bits(39..48, u64::from(p4_index)); | ||||
|         addr.set_bits(30..39, u64::from(p3_index)); | ||||
|         addr.set_bits(21..30, u64::from(p2_index)); | ||||
|         addr.set_bits(12..21, u64::from(p1_index)); | ||||
|         Page::containing_address(VirtAddr::new(addr)) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the level 1 page table index of this page.
 | ||||
|     pub fn p1_index(&self) -> u9 { | ||||
|         self.start_address().p1_index() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> fmt::Debug for Page<S> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_fmt(format_args!( | ||||
|             "Page[{}]({:#x})", | ||||
|             S::SIZE_AS_DEBUG_STR, | ||||
|             self.start_address().as_u64() | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> Add<u64> for Page<S> { | ||||
|     type Output = Self; | ||||
|     fn add(self, rhs: u64) -> Self::Output { | ||||
|         Page::containing_address(self.start_address() + rhs * u64::from(S::SIZE)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> AddAssign<u64> for Page<S> { | ||||
|     fn add_assign(&mut self, rhs: u64) { | ||||
|         *self = self.clone() + rhs; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> Sub<u64> for Page<S> { | ||||
|     type Output = Self; | ||||
|     fn sub(self, rhs: u64) -> Self::Output { | ||||
|         Page::containing_address(self.start_address() - rhs * u64::from(S::SIZE)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> SubAssign<u64> for Page<S> { | ||||
|     fn sub_assign(&mut self, rhs: u64) { | ||||
|         *self = self.clone() - rhs; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> Sub<Self> for Page<S> { | ||||
|     type Output = u64; | ||||
|     fn sub(self, rhs: Self) -> Self::Output { | ||||
|         (self.start_address - rhs.start_address) / S::SIZE | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// A range of pages with exclusive upper bound.
 | ||||
| #[derive(Clone, Copy, PartialEq, Eq)] | ||||
| #[repr(C)] | ||||
| pub struct PageRange<S: PageSize = Size4KiB> { | ||||
|     /// The start of the range, inclusive.
 | ||||
|     pub start: Page<S>, | ||||
|     /// The end of the range, exclusive.
 | ||||
|     pub end: Page<S>, | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> PageRange<S> { | ||||
|     /// Returns wether this range contains no pages.
 | ||||
|     pub fn is_empty(&self) -> bool { | ||||
|         !(self.start < self.end) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> Iterator for PageRange<S> { | ||||
|     type Item = Page<S>; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         if self.start < self.end { | ||||
|             let page = self.start.clone(); | ||||
|             self.start += 1; | ||||
|             Some(page) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl PageRange<Size2MiB> { | ||||
|     /// Converts the range of 2MiB pages to a range of 4KiB pages.
 | ||||
|     pub fn as_4kib_page_range(self) -> PageRange<Size4KiB> { | ||||
|         PageRange { | ||||
|             start: Page::containing_address(self.start.start_address()), | ||||
|             end: Page::containing_address(self.end.start_address()), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> fmt::Debug for PageRange<S> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("PageRange") | ||||
|             .field("start", &self.start) | ||||
|             .field("end", &self.end) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// A range of pages with inclusive upper bound.
 | ||||
| #[derive(Clone, Copy, PartialEq, Eq)] | ||||
| #[repr(C)] | ||||
| pub struct PageRangeInclusive<S: PageSize = Size4KiB> { | ||||
|     /// The start of the range, inclusive.
 | ||||
|     pub start: Page<S>, | ||||
|     /// The end of the range, inclusive.
 | ||||
|     pub end: Page<S>, | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> PageRangeInclusive<S> { | ||||
|     /// Returns wether this range contains no pages.
 | ||||
|     pub fn is_empty(&self) -> bool { | ||||
|         !(self.start <= self.end) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> Iterator for PageRangeInclusive<S> { | ||||
|     type Item = Page<S>; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         if self.start <= self.end { | ||||
|             let page = self.start.clone(); | ||||
|             self.start += 1; | ||||
|             Some(page) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> fmt::Debug for PageRangeInclusive<S> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("PageRangeInclusive") | ||||
|             .field("start", &self.start) | ||||
|             .field("end", &self.end) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// A physical memory frame.
 | ||||
| #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] | ||||
| #[repr(C)] | ||||
| pub struct PhysFrame<S: PageSize = Size4KiB> { | ||||
|     start_address: PhysAddr, | ||||
|     size: PhantomData<S>, | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> PhysFrame<S> { | ||||
|     /// Returns the frame that starts at the given virtual address.
 | ||||
|     ///
 | ||||
|     /// Returns an error if the address is not correctly aligned (i.e. is not a valid frame start).
 | ||||
|     pub fn from_start_address(address: PhysAddr) -> Result<Self, ()> { | ||||
|         if !address.is_aligned(S::SIZE) { | ||||
|             return Err(()); | ||||
|         } | ||||
|         Ok(PhysFrame::containing_address(address)) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the frame that contains the given physical address.
 | ||||
|     pub fn containing_address(address: PhysAddr) -> Self { | ||||
|         PhysFrame { | ||||
|             start_address: address.align_down(S::SIZE), | ||||
|             size: PhantomData, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the start address of the frame.
 | ||||
|     pub fn start_address(&self) -> PhysAddr { | ||||
|         self.start_address | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the size the frame (4KB, 2MB or 1GB).
 | ||||
|     pub fn size(&self) -> u64 { | ||||
|         S::SIZE | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a range of frames, exclusive `end`.
 | ||||
|     pub fn range(start: PhysFrame<S>, end: PhysFrame<S>) -> PhysFrameRange<S> { | ||||
|         PhysFrameRange { start, end } | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a range of frames, inclusive `end`.
 | ||||
|     pub fn range_inclusive(start: PhysFrame<S>, end: PhysFrame<S>) -> PhysFrameRangeInclusive<S> { | ||||
|         PhysFrameRangeInclusive { start, end } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> fmt::Debug for PhysFrame<S> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.write_fmt(format_args!( | ||||
|             "PhysFrame[{}]({:#x})", | ||||
|             S::SIZE_AS_DEBUG_STR, | ||||
|             self.start_address().as_u64() | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> Add<u64> for PhysFrame<S> { | ||||
|     type Output = Self; | ||||
|     fn add(self, rhs: u64) -> Self::Output { | ||||
|         PhysFrame::containing_address(self.start_address() + rhs * u64::from(S::SIZE)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> AddAssign<u64> for PhysFrame<S> { | ||||
|     fn add_assign(&mut self, rhs: u64) { | ||||
|         *self = self.clone() + rhs; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> Sub<u64> for PhysFrame<S> { | ||||
|     type Output = Self; | ||||
|     fn sub(self, rhs: u64) -> Self::Output { | ||||
|         PhysFrame::containing_address(self.start_address() - rhs * u64::from(S::SIZE)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> SubAssign<u64> for PhysFrame<S> { | ||||
|     fn sub_assign(&mut self, rhs: u64) { | ||||
|         *self = self.clone() - rhs; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> Sub<PhysFrame<S>> for PhysFrame<S> { | ||||
|     type Output = u64; | ||||
|     fn sub(self, rhs: PhysFrame<S>) -> Self::Output { | ||||
|         (self.start_address - rhs.start_address) / S::SIZE | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// An range of physical memory frames, exclusive the upper bound.
 | ||||
| #[derive(Clone, Copy, PartialEq, Eq)] | ||||
| #[repr(C)] | ||||
| pub struct PhysFrameRange<S: PageSize = Size4KiB> { | ||||
|     /// The start of the range, inclusive.
 | ||||
|     pub start: PhysFrame<S>, | ||||
|     /// The end of the range, exclusive.
 | ||||
|     pub end: PhysFrame<S>, | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> PhysFrameRange<S> { | ||||
|     /// Returns whether the range contains no frames.
 | ||||
|     pub fn is_empty(&self) -> bool { | ||||
|         !(self.start < self.end) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> Iterator for PhysFrameRange<S> { | ||||
|     type Item = PhysFrame<S>; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         if self.start < self.end { | ||||
|             let frame = self.start.clone(); | ||||
|             self.start += 1; | ||||
|             Some(frame) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<os_bootinfo::FrameRange> for PhysFrameRange { | ||||
|     fn from(range: os_bootinfo::FrameRange) -> Self { | ||||
|         PhysFrameRange { | ||||
|             start: PhysFrame::from_start_address(PhysAddr::new(range.start_addr())).unwrap(), | ||||
|             end: PhysFrame::from_start_address(PhysAddr::new(range.end_addr())).unwrap(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Into<os_bootinfo::FrameRange> for PhysFrameRange { | ||||
|     fn into(self) -> os_bootinfo::FrameRange { | ||||
|         os_bootinfo::FrameRange::new( | ||||
|             self.start.start_address().as_u64(), | ||||
|             self.end.start_address().as_u64(), | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> fmt::Debug for PhysFrameRange<S> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("PhysFrameRange") | ||||
|             .field("start", &self.start) | ||||
|             .field("end", &self.end) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// An range of physical memory frames, inclusive the upper bound.
 | ||||
| #[derive(Clone, Copy, PartialEq, Eq)] | ||||
| #[repr(C)] | ||||
| pub struct PhysFrameRangeInclusive<S: PageSize = Size4KiB> { | ||||
|     /// The start of the range, inclusive.
 | ||||
|     pub start: PhysFrame<S>, | ||||
|     /// The start of the range, exclusive.
 | ||||
|     pub end: PhysFrame<S>, | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> PhysFrameRangeInclusive<S> { | ||||
|     /// Returns whether the range contains no frames.
 | ||||
|     pub fn is_empty(&self) -> bool { | ||||
|         !(self.start <= self.end) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> Iterator for PhysFrameRangeInclusive<S> { | ||||
|     type Item = PhysFrame<S>; | ||||
| 
 | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         if self.start <= self.end { | ||||
|             let frame = self.start.clone(); | ||||
|             self.start += 1; | ||||
|             Some(frame) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<S: PageSize> fmt::Debug for PhysFrameRangeInclusive<S> { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         f.debug_struct("PhysFrameRangeInclusive") | ||||
|             .field("start", &self.start) | ||||
|             .field("end", &self.end) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
| 
 | ||||
|     #[test] | ||||
|     pub fn test_page_ranges() { | ||||
|         let page_size = Size4KiB::SIZE; | ||||
|         let number = 1000; | ||||
| 
 | ||||
|         let start_addr = VirtAddr::new(0xdeadbeaf); | ||||
|         let start: Page = Page::containing_address(start_addr); | ||||
|         let end = start.clone() + number; | ||||
| 
 | ||||
|         let mut range = Page::range(start.clone(), end.clone()); | ||||
|         for i in 0..number { | ||||
|             assert_eq!( | ||||
|                 range.next(), | ||||
|                 Some(Page::containing_address(start_addr + page_size * i)) | ||||
|             ); | ||||
|         } | ||||
|         assert_eq!(range.next(), None); | ||||
| 
 | ||||
|         let mut range_inclusive = Page::range_inclusive(start, end); | ||||
|         for i in 0..=number { | ||||
|             assert_eq!( | ||||
|                 range_inclusive.next(), | ||||
|                 Some(Page::containing_address(start_addr + page_size * i)) | ||||
|             ); | ||||
|         } | ||||
|         assert_eq!(range_inclusive.next(), None); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,185 @@ | ||||
| use core::fmt; | ||||
| use core::ops::{Index, IndexMut}; | ||||
| 
 | ||||
| use super::{PageSize, PhysFrame, Size4KiB}; | ||||
| use addr::PhysAddr; | ||||
| 
 | ||||
| use usize_conversions::usize_from; | ||||
| use ux::*; | ||||
| 
 | ||||
| /// The error returned by the `PageTableEntry::frame` method.
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq)] | ||||
| pub enum FrameError { | ||||
|     /// The entry does not have the `PRESENT` flag set, so it isn't currently mapped to a frame.
 | ||||
|     FrameNotPresent, | ||||
|     /// The entry does have the `HUGE_PAGE` flag set. The `frame` method has a standard 4KiB frame
 | ||||
|     /// as return type, so a huge frame can't be returned.
 | ||||
|     HugeFrame, | ||||
| } | ||||
| 
 | ||||
| /// A 64-bit page table entry.
 | ||||
| #[derive(Clone)] | ||||
| #[repr(transparent)] | ||||
| pub struct PageTableEntry { | ||||
|     entry: u64, | ||||
| } | ||||
| 
 | ||||
| impl PageTableEntry { | ||||
|     /// Returns whether this entry is zero.
 | ||||
|     pub fn is_unused(&self) -> bool { | ||||
|         self.entry == 0 | ||||
|     } | ||||
| 
 | ||||
|     /// Sets this entry to zero.
 | ||||
|     pub fn set_unused(&mut self) { | ||||
|         self.entry = 0; | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the flags of this entry.
 | ||||
|     pub fn flags(&self) -> PageTableFlags { | ||||
|         PageTableFlags::from_bits_truncate(self.entry) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the physical address mapped by this entry, might be zero.
 | ||||
|     pub fn addr(&self) -> PhysAddr { | ||||
|         PhysAddr::new(self.entry & 0x000fffff_fffff000) | ||||
|     } | ||||
| 
 | ||||
|     /// Returns the physical frame mapped by this entry.
 | ||||
|     ///
 | ||||
|     /// Returns the following errors:
 | ||||
|     ///
 | ||||
|     /// - `FrameError::FrameNotPresent` if the entry doesn't have the `PRESENT` flag set.
 | ||||
|     /// - `FrameError::HugeFrame` if the entry has the `HUGE_PAGE` flag set (for huge pages the
 | ||||
|     ///    `addr` function must be used)
 | ||||
|     pub fn frame(&self) -> Result<PhysFrame, FrameError> { | ||||
|         if !self.flags().contains(PageTableFlags::PRESENT) { | ||||
|             Err(FrameError::FrameNotPresent) | ||||
|         } else if self.flags().contains(PageTableFlags::HUGE_PAGE) { | ||||
|             Err(FrameError::HugeFrame) | ||||
|         } else { | ||||
|             Ok(PhysFrame::containing_address(self.addr())) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Map the entry to the specified physical address with the specified flags.
 | ||||
|     pub fn set_addr(&mut self, addr: PhysAddr, flags: PageTableFlags) { | ||||
|         assert!(addr.is_aligned(Size4KiB::SIZE)); | ||||
|         self.entry = (addr.as_u64()) | flags.bits(); | ||||
|     } | ||||
| 
 | ||||
|     /// Map the entry to the specified physical frame with the specified flags.
 | ||||
|     pub fn set_frame(&mut self, frame: PhysFrame, flags: PageTableFlags) { | ||||
|         assert!(!flags.contains(PageTableFlags::HUGE_PAGE)); | ||||
|         self.set_addr(frame.start_address(), flags) | ||||
|     } | ||||
| 
 | ||||
|     /// Sets the flags of this entry.
 | ||||
|     pub fn set_flags(&mut self, flags: PageTableFlags) { | ||||
|         self.entry = self.addr().as_u64() | flags.bits(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for PageTableEntry { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         let mut f = f.debug_struct("PageTableEntry"); | ||||
|         f.field("addr", &self.addr()); | ||||
|         f.field("flags", &self.flags()); | ||||
|         f.finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bitflags! { | ||||
|     /// Possible flags for a page table entry.
 | ||||
|     pub struct PageTableFlags: u64 { | ||||
|         const ALL =             0xffffffff_ffffffff; | ||||
|         const TYPE_MASK =       3 << 0; | ||||
|         const TYPE_FAULT =      0 << 0; | ||||
|         const TYPE_PAGE =       3 << 0; | ||||
|         const TABLE_BIT =       1 << 1; | ||||
| 
 | ||||
|         const PRESENT =         1 << 0; | ||||
|         const USER_ACCESSIBLE = 1 << 6;         /* AP[1] */ | ||||
|         const RDONLY =          1 << 7;         /* AP[2] */ | ||||
|         const SHARED =          3 << 8;         /* SH[1:0], inner shareable */ | ||||
|         const BIT_8 =           1 << 8; | ||||
|         const BIT_9 =           1 << 9; | ||||
|         /* | ||||
|             pub const ATTRIB_SH_NON_SHAREABLE: usize = 0x0 << 8; | ||||
|             pub const ATTRIB_SH_OUTER_SHAREABLE: usize = 0x2 << 8; | ||||
|             pub const ATTRIB_SH_INNER_SHAREABLE: usize = 0x3 << 8; | ||||
|         */ | ||||
| 
 | ||||
|         const ACCESSED =        1 << 10;        /* AF, Access Flag */ | ||||
|         const NONE_GLOBAL =     1 << 11;        /* None Global */ | ||||
|         const GLOBAL =          (!(1 << 11)); | ||||
|         const DBM =             1 << 51;        /* Dirty Bit Management */ | ||||
|         const WRITE =           1 << 51;        /* DBM */ | ||||
|         const CONT =            1 << 52;        /* Contiguous range */ | ||||
|         const PXN =             1 << 53;        /* Privileged XN */ | ||||
|         const UXN =             1 << 54;        /* User XN */ | ||||
|         const HYP_XN =          1 << 54;        /* HYP XN */ | ||||
|         const DIRTY =           1 << 55; | ||||
|         const SWAPPED =         1 << 56; | ||||
|         const HUGE_PAGE =       1 << 57; | ||||
|         const PROT_NONE =       1 << 58; | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// The number of entries in a page table.
 | ||||
| const ENTRY_COUNT: usize = 512; | ||||
| 
 | ||||
| /// Represents a page table.
 | ||||
| ///
 | ||||
| /// Always page-sized.
 | ||||
| ///
 | ||||
| /// This struct implements the `Index` and `IndexMut` traits, so the entries can be accessed
 | ||||
| /// through index operations. For example, `page_table[15]` returns the 15th page table entry.
 | ||||
| #[repr(transparent)] | ||||
| pub struct PageTable { | ||||
|     entries: [PageTableEntry; ENTRY_COUNT], | ||||
| } | ||||
| 
 | ||||
| impl PageTable { | ||||
|     /// Clears all entries.
 | ||||
|     pub fn zero(&mut self) { | ||||
|         for entry in self.entries.iter_mut() { | ||||
|             entry.set_unused(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Index<usize> for PageTable { | ||||
|     type Output = PageTableEntry; | ||||
| 
 | ||||
|     fn index(&self, index: usize) -> &Self::Output { | ||||
|         &self.entries[index] | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl IndexMut<usize> for PageTable { | ||||
|     fn index_mut(&mut self, index: usize) -> &mut Self::Output { | ||||
|         &mut self.entries[index] | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Index<u9> for PageTable { | ||||
|     type Output = PageTableEntry; | ||||
| 
 | ||||
|     fn index(&self, index: u9) -> &Self::Output { | ||||
|         &self.entries[usize_from(u16::from(index))] | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl IndexMut<u9> for PageTable { | ||||
|     fn index_mut(&mut self, index: u9) -> &mut Self::Output { | ||||
|         &mut self.entries[usize_from(u16::from(index))] | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl fmt::Debug for PageTable { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||
|         self.entries[..].fmt(f) | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,623 @@ | ||||
| #![cfg(target_arch = "aarch64")] | ||||
| 
 | ||||
| use asm::tlb_invalidate; | ||||
| use paging::{ | ||||
|     frame_alloc::FrameAllocator, | ||||
|     page_table::{FrameError, PageTable, PageTableEntry, PageTableFlags}, | ||||
|     NotGiantPageSize, Page, PageSize, PhysFrame, Size1GiB, Size2MiB, Size4KiB, | ||||
| }; | ||||
| use paging::page_table::PageTableFlags as Flags; | ||||
| use asm::ttbr0_el1_read; | ||||
| use ux::u9; | ||||
| use addr::{PhysAddr, VirtAddr}; | ||||
| 
 | ||||
| /// This type represents a page whose mapping has changed in the page table.
 | ||||
| ///
 | ||||
| /// The old mapping might be still cached in the translation lookaside buffer (TLB), so it needs
 | ||||
| /// to be flushed from the TLB before it's accessed. This type is returned from function that
 | ||||
| /// change the mapping of a page to ensure that the TLB flush is not forgotten.
 | ||||
| #[derive(Debug)] | ||||
| #[must_use = "Page Table changes must be flushed or ignored."] | ||||
| pub struct MapperFlush<S: PageSize>(Page<S>); | ||||
| 
 | ||||
| impl<S: PageSize> MapperFlush<S> { | ||||
|     /// Create a new flush promise
 | ||||
|     fn new(page: Page<S>) -> Self { | ||||
|         MapperFlush(page) | ||||
|     } | ||||
| 
 | ||||
|     /// Flush the page from the TLB to ensure that the newest mapping is used.
 | ||||
|     pub fn flush(self) { | ||||
|         tlb_invalidate(); | ||||
|     } | ||||
| 
 | ||||
|     /// Don't flush the TLB and silence the “must be used” warning.
 | ||||
|     pub fn ignore(self) {} | ||||
| } | ||||
| 
 | ||||
| /// A trait for common page table operations.
 | ||||
| pub trait Mapper<S: PageSize> { | ||||
|     /// Creates a new mapping in the page table.
 | ||||
|     ///
 | ||||
|     /// This function might need additional physical frames to create new page tables. These
 | ||||
|     /// frames are allocated from the `allocator` argument. At most three frames are required.
 | ||||
|     fn map_to<A>( | ||||
|         &mut self, | ||||
|         page: Page<S>, | ||||
|         frame: PhysFrame<S>, | ||||
|         flags: PageTableFlags, | ||||
|         allocator: &mut A, | ||||
|     ) -> Result<MapperFlush<S>, MapToError> | ||||
|     where | ||||
|         A: FrameAllocator<Size4KiB>; | ||||
| 
 | ||||
|     /// Removes a mapping from the page table and returns the frame that used to be mapped.
 | ||||
|     ///
 | ||||
|     /// Note that no page tables or pages are deallocated.
 | ||||
|     fn unmap(&mut self, page: Page<S>) -> Result<(PhysFrame<S>, MapperFlush<S>), UnmapError>; | ||||
| 
 | ||||
|     /// Updates the flags of an existing mapping.
 | ||||
|     fn update_flags( | ||||
|         &mut self, | ||||
|         page: Page<S>, | ||||
|         flags: PageTableFlags, | ||||
|     ) -> Result<MapperFlush<S>, FlagUpdateError>; | ||||
| 
 | ||||
|     /// Return the frame that the specified page is mapped to.
 | ||||
|     fn translate_page(&self, page: Page<S>) -> Option<PhysFrame<S>>; | ||||
| 
 | ||||
|     /// Maps the given frame to the virtual page with the same address.
 | ||||
|     fn identity_map<A>( | ||||
|         &mut self, | ||||
|         frame: PhysFrame<S>, | ||||
|         flags: PageTableFlags, | ||||
|         allocator: &mut A, | ||||
|     ) -> Result<MapperFlush<S>, MapToError> | ||||
|     where | ||||
|         A: FrameAllocator<Size4KiB>, | ||||
|         S: PageSize, | ||||
|         Self: Mapper<S>, | ||||
|     { | ||||
|         let page = Page::containing_address(VirtAddr::new(frame.start_address().as_u64())); | ||||
|         self.map_to(page, frame, flags, allocator) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// A recursive page table is a last level page table with an entry mapped to the table itself.
 | ||||
| ///
 | ||||
| /// This recursive mapping allows accessing all page tables in the hierarchy:
 | ||||
| ///
 | ||||
| /// - To access the level 4 page table, we “loop“ (i.e. follow the recursively mapped entry) four
 | ||||
| ///   times.
 | ||||
| /// - To access a level 3 page table, we “loop” three times and then use the level 4 index.
 | ||||
| /// - To access a level 2 page table, we “loop” two times, then use the level 4 index, then the
 | ||||
| ///   level 3 index.
 | ||||
| /// - To access a level 1 page table, we “loop” once, then use the level 4 index, then the
 | ||||
| ///   level 3 index, then the level 2 index.
 | ||||
| ///
 | ||||
| /// This struct implements the `Mapper` trait.
 | ||||
| #[derive(Debug)] | ||||
| pub struct RecursivePageTable<'a> { | ||||
|     p4: &'a mut PageTable, | ||||
|     recursive_index: u9, | ||||
| } | ||||
| 
 | ||||
| /// An error indicating that the given page table is not recursively mapped.
 | ||||
| ///
 | ||||
| /// Returned from `RecursivePageTable::new`.
 | ||||
| #[derive(Debug)] | ||||
| pub struct NotRecursivelyMapped; | ||||
| 
 | ||||
| /// This error is returned from `map_to` and similar methods.
 | ||||
| #[derive(Debug)] | ||||
| pub enum MapToError { | ||||
|     /// An additional frame was needed for the mapping process, but the frame allocator
 | ||||
|     /// returned `None`.
 | ||||
|     FrameAllocationFailed, | ||||
|     /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
 | ||||
|     /// given page is part of an already mapped huge page.
 | ||||
|     ParentEntryHugePage, | ||||
|     /// The given page is already mapped to a physical frame.
 | ||||
|     PageAlreadyMapped, | ||||
| } | ||||
| 
 | ||||
| /// An error indicating that an `unmap` call failed.
 | ||||
| #[derive(Debug)] | ||||
| pub enum UnmapError { | ||||
|     /// An upper level page table entry has the `HUGE_PAGE` flag set, which means that the
 | ||||
|     /// given page is part of a huge page and can't be freed individually.
 | ||||
|     ParentEntryHugePage, | ||||
|     /// The given page is not mapped to a physical frame.
 | ||||
|     PageNotMapped, | ||||
|     /// The page table entry for the given page points to an invalid physical address.
 | ||||
|     InvalidFrameAddress(PhysAddr), | ||||
| } | ||||
| 
 | ||||
| /// An error indicating that an `update_flags` call failed.
 | ||||
| #[derive(Debug)] | ||||
| pub enum FlagUpdateError { | ||||
|     /// The given page is not mapped to a physical frame.
 | ||||
|     PageNotMapped, | ||||
| } | ||||
| 
 | ||||
| impl<'a> RecursivePageTable<'a> { | ||||
|     /// Creates a new RecursivePageTable from the passed level 4 PageTable.
 | ||||
|     ///
 | ||||
|     /// The page table must be recursively mapped, that means:
 | ||||
|     ///
 | ||||
|     /// - The page table must have one recursive entry, i.e. an entry that points to the table
 | ||||
|     ///   itself.
 | ||||
|     ///     - The reference must use that “loop”, i.e. be of the form `0o_xxx_xxx_xxx_xxx_0000`
 | ||||
|     ///       where `xxx` is the recursive entry.
 | ||||
|     /// - The page table must be active, i.e. the CR3 register must contain its physical address.
 | ||||
|     ///
 | ||||
|     /// Otherwise `Err(NotRecursivelyMapped)` is returned.
 | ||||
|     pub fn new(table: &'a mut PageTable) -> Result<Self, NotRecursivelyMapped> { | ||||
|         let page = Page::containing_address(VirtAddr::new(table as *const _ as u64)); | ||||
|         let recursive_index = page.p4_index(); | ||||
| 
 | ||||
|         if page.p3_index() != recursive_index | ||||
|             || page.p2_index() != recursive_index | ||||
|             || page.p1_index() != recursive_index | ||||
|         { | ||||
|             return Err(NotRecursivelyMapped); | ||||
|         } | ||||
|         if Ok(ttbr0_el1_read().0) != table[recursive_index].frame() { | ||||
|             return Err(NotRecursivelyMapped); | ||||
|         } | ||||
| 
 | ||||
|         Ok(RecursivePageTable { | ||||
|             p4: table, | ||||
|             recursive_index, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     /// Creates a new RecursivePageTable without performing any checks.
 | ||||
|     ///
 | ||||
|     /// The `recursive_index` parameter must be the index of the recursively mapped entry.
 | ||||
|     pub unsafe fn new_unchecked(table: &'a mut PageTable, recursive_index: u9) -> Self { | ||||
|         RecursivePageTable { | ||||
|             p4: table, | ||||
|             recursive_index, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Internal helper function to create the page table of the next level if needed.
 | ||||
|     ///
 | ||||
|     /// If the passed entry is unused, a new frame is allocated from the given allocator, zeroed,
 | ||||
|     /// and the entry is updated to that address. If the passed entry is already mapped, the next
 | ||||
|     /// table is returned directly.
 | ||||
|     ///
 | ||||
|     /// The `next_page_table` page must be the page of the next page table in the hierarchy.
 | ||||
|     ///
 | ||||
|     /// Returns `MapToError::FrameAllocationFailed` if the entry is unused and the allocator
 | ||||
|     /// returned `None`. Returns `MapToError::ParentEntryHugePage` if the `HUGE_PAGE` flag is set
 | ||||
|     /// in the passed entry.
 | ||||
|     unsafe fn create_next_table<'b, A>( | ||||
|         entry: &'b mut PageTableEntry, | ||||
|         next_table_page: Page, | ||||
|         allocator: &mut A, | ||||
|     ) -> Result<&'b mut PageTable, MapToError> | ||||
|     where | ||||
|         A: FrameAllocator<Size4KiB>, | ||||
|     { | ||||
|         /// This inner function is used to limit the scope of `unsafe`.
 | ||||
|         ///
 | ||||
|         /// This is a safe function, so we need to use `unsafe` blocks when we do something unsafe.
 | ||||
|         fn inner<'b, A>( | ||||
|             entry: &'b mut PageTableEntry, | ||||
|             next_table_page: Page, | ||||
|             allocator: &mut A, | ||||
|         ) -> Result<&'b mut PageTable, MapToError> | ||||
|         where | ||||
|             A: FrameAllocator<Size4KiB>, | ||||
|         { | ||||
| 
 | ||||
|             let created; | ||||
| 
 | ||||
|             if entry.is_unused() { | ||||
|                 if let Some(frame) = allocator.alloc() { | ||||
|                     entry.set_frame(frame, Flags::PRESENT | Flags::WRITE); | ||||
|                     created = true; | ||||
|                 } else { | ||||
|                     return Err(MapToError::FrameAllocationFailed); | ||||
|                 } | ||||
|             } else { | ||||
|                 created = false; | ||||
|             } | ||||
|             if entry.flags().contains(Flags::HUGE_PAGE) { | ||||
|                 return Err(MapToError::ParentEntryHugePage); | ||||
|             } | ||||
| 
 | ||||
|             let page_table_ptr = next_table_page.start_address().as_mut_ptr(); | ||||
|             let page_table: &mut PageTable = unsafe { &mut *(page_table_ptr) }; | ||||
|             if created { | ||||
|                 page_table.zero(); | ||||
|             } | ||||
|             Ok(page_table) | ||||
|         } | ||||
| 
 | ||||
|         inner(entry, next_table_page, allocator) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> Mapper<Size1GiB> for RecursivePageTable<'a> { | ||||
|     fn map_to<A>( | ||||
|         &mut self, | ||||
|         page: Page<Size1GiB>, | ||||
|         frame: PhysFrame<Size1GiB>, | ||||
|         flags: PageTableFlags, | ||||
|         allocator: &mut A, | ||||
|     ) -> Result<MapperFlush<Size1GiB>, MapToError> | ||||
|     where | ||||
|         A: FrameAllocator<Size4KiB>, | ||||
|     { | ||||
|         let p4 = &mut self.p4; | ||||
| 
 | ||||
|         let p3_page = p3_page(page, self.recursive_index); | ||||
|         let p3 = unsafe { Self::create_next_table(&mut p4[page.p4_index()], p3_page, allocator)? }; | ||||
| 
 | ||||
|         if !p3[page.p3_index()].is_unused() { | ||||
|             return Err(MapToError::PageAlreadyMapped); | ||||
|         } | ||||
|         p3[page.p3_index()].set_addr(frame.start_address(), flags | Flags::HUGE_PAGE); | ||||
| 
 | ||||
|         Ok(MapperFlush::new(page)) | ||||
|     } | ||||
| 
 | ||||
|     fn unmap( | ||||
|         &mut self, | ||||
|         page: Page<Size1GiB>, | ||||
|     ) -> Result<(PhysFrame<Size1GiB>, MapperFlush<Size1GiB>), UnmapError> { | ||||
|         let p4 = &mut self.p4; | ||||
|         let p4_entry = &p4[page.p4_index()]; | ||||
| 
 | ||||
|         p4_entry.frame().map_err(|err| match err { | ||||
|             FrameError::FrameNotPresent => UnmapError::PageNotMapped, | ||||
|             FrameError::HugeFrame => UnmapError::ParentEntryHugePage, | ||||
|         })?; | ||||
| 
 | ||||
|         let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; | ||||
|         let p3_entry = &mut p3[page.p3_index()]; | ||||
|         let flags = p3_entry.flags(); | ||||
| 
 | ||||
|         if !flags.contains(PageTableFlags::PRESENT) { | ||||
|             return Err(UnmapError::PageNotMapped); | ||||
|         } | ||||
|         if !flags.contains(PageTableFlags::HUGE_PAGE) { | ||||
|             return Err(UnmapError::ParentEntryHugePage); | ||||
|         } | ||||
| 
 | ||||
|         let frame = PhysFrame::from_start_address(p3_entry.addr()) | ||||
|             .map_err(|()| UnmapError::InvalidFrameAddress(p3_entry.addr()))?; | ||||
| 
 | ||||
|         p3_entry.set_unused(); | ||||
|         Ok((frame, MapperFlush::new(page))) | ||||
|     } | ||||
| 
 | ||||
|     fn update_flags( | ||||
|         &mut self, | ||||
|         page: Page<Size1GiB>, | ||||
|         flags: PageTableFlags, | ||||
|     ) -> Result<MapperFlush<Size1GiB>, FlagUpdateError> { | ||||
|         let p4 = &mut self.p4; | ||||
| 
 | ||||
|         if p4[page.p4_index()].is_unused() { | ||||
|             return Err(FlagUpdateError::PageNotMapped); | ||||
|         } | ||||
| 
 | ||||
|         let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; | ||||
| 
 | ||||
|         if p3[page.p3_index()].is_unused() { | ||||
|             return Err(FlagUpdateError::PageNotMapped); | ||||
|         } | ||||
|         p3[page.p3_index()].set_flags(flags | Flags::HUGE_PAGE); | ||||
| 
 | ||||
|         Ok(MapperFlush::new(page)) | ||||
|     } | ||||
| 
 | ||||
|     fn translate_page(&self, page: Page<Size1GiB>) -> Option<PhysFrame<Size1GiB>> { | ||||
|         let p4 = &self.p4; | ||||
| 
 | ||||
|         if p4[page.p4_index()].is_unused() { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         let p3 = unsafe { &*(p3_ptr(page, self.recursive_index)) }; | ||||
|         let p3_entry = &p3[page.p3_index()]; | ||||
| 
 | ||||
|         if p3_entry.is_unused() { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         PhysFrame::from_start_address(p3_entry.addr()).ok() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> Mapper<Size2MiB> for RecursivePageTable<'a> { | ||||
|     fn map_to<A>( | ||||
|         &mut self, | ||||
|         page: Page<Size2MiB>, | ||||
|         frame: PhysFrame<Size2MiB>, | ||||
|         flags: PageTableFlags, | ||||
|         allocator: &mut A, | ||||
|     ) -> Result<MapperFlush<Size2MiB>, MapToError> | ||||
|     where | ||||
|         A: FrameAllocator<Size4KiB>, | ||||
|     { | ||||
|         let p4 = &mut self.p4; | ||||
| 
 | ||||
|         let p3_page = p3_page(page, self.recursive_index); | ||||
|         let p3 = unsafe { Self::create_next_table(&mut p4[page.p4_index()], p3_page, allocator)? }; | ||||
| 
 | ||||
|         let p2_page = p2_page(page, self.recursive_index); | ||||
|         let p2 = unsafe { Self::create_next_table(&mut p3[page.p3_index()], p2_page, allocator)? }; | ||||
| 
 | ||||
|         if !p2[page.p2_index()].is_unused() { | ||||
|             return Err(MapToError::PageAlreadyMapped); | ||||
|         } | ||||
|         p2[page.p2_index()].set_addr(frame.start_address(), flags | Flags::HUGE_PAGE); | ||||
| 
 | ||||
|         Ok(MapperFlush::new(page)) | ||||
|     } | ||||
| 
 | ||||
|     fn unmap( | ||||
|         &mut self, | ||||
|         page: Page<Size2MiB>, | ||||
|     ) -> Result<(PhysFrame<Size2MiB>, MapperFlush<Size2MiB>), UnmapError> { | ||||
|         let p4 = &mut self.p4; | ||||
|         let p4_entry = &p4[page.p4_index()]; | ||||
|         p4_entry.frame().map_err(|err| match err { | ||||
|             FrameError::FrameNotPresent => UnmapError::PageNotMapped, | ||||
|             FrameError::HugeFrame => UnmapError::ParentEntryHugePage, | ||||
|         })?; | ||||
| 
 | ||||
|         let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; | ||||
|         let p3_entry = &p3[page.p3_index()]; | ||||
|         p3_entry.frame().map_err(|err| match err { | ||||
|             FrameError::FrameNotPresent => UnmapError::PageNotMapped, | ||||
|             FrameError::HugeFrame => UnmapError::ParentEntryHugePage, | ||||
|         })?; | ||||
| 
 | ||||
|         let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; | ||||
|         let p2_entry = &mut p2[page.p2_index()]; | ||||
|         let flags = p2_entry.flags(); | ||||
| 
 | ||||
|         if !flags.contains(PageTableFlags::PRESENT) { | ||||
|             return Err(UnmapError::PageNotMapped); | ||||
|         } | ||||
|         if !flags.contains(PageTableFlags::HUGE_PAGE) { | ||||
|             return Err(UnmapError::ParentEntryHugePage); | ||||
|         } | ||||
| 
 | ||||
|         let frame = PhysFrame::from_start_address(p2_entry.addr()) | ||||
|             .map_err(|()| UnmapError::InvalidFrameAddress(p2_entry.addr()))?; | ||||
| 
 | ||||
|         p2_entry.set_unused(); | ||||
|         Ok((frame, MapperFlush::new(page))) | ||||
|     } | ||||
| 
 | ||||
|     fn update_flags( | ||||
|         &mut self, | ||||
|         page: Page<Size2MiB>, | ||||
|         flags: PageTableFlags, | ||||
|     ) -> Result<MapperFlush<Size2MiB>, FlagUpdateError> { | ||||
|         let p4 = &mut self.p4; | ||||
| 
 | ||||
|         if p4[page.p4_index()].is_unused() { | ||||
|             return Err(FlagUpdateError::PageNotMapped); | ||||
|         } | ||||
| 
 | ||||
|         let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; | ||||
| 
 | ||||
|         if p3[page.p3_index()].is_unused() { | ||||
|             return Err(FlagUpdateError::PageNotMapped); | ||||
|         } | ||||
| 
 | ||||
|         let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; | ||||
| 
 | ||||
|         if p2[page.p2_index()].is_unused() { | ||||
|             return Err(FlagUpdateError::PageNotMapped); | ||||
|         } | ||||
| 
 | ||||
|         p2[page.p2_index()].set_flags(flags | Flags::HUGE_PAGE); | ||||
| 
 | ||||
|         Ok(MapperFlush::new(page)) | ||||
|     } | ||||
| 
 | ||||
|     fn translate_page(&self, page: Page<Size2MiB>) -> Option<PhysFrame<Size2MiB>> { | ||||
|         let p4 = &self.p4; | ||||
| 
 | ||||
|         if p4[page.p4_index()].is_unused() { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         let p3 = unsafe { &*(p3_ptr(page, self.recursive_index)) }; | ||||
|         let p3_entry = &p3[page.p3_index()]; | ||||
| 
 | ||||
|         if p3_entry.is_unused() { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         let p2 = unsafe { &*(p2_ptr(page, self.recursive_index)) }; | ||||
|         let p2_entry = &p2[page.p2_index()]; | ||||
| 
 | ||||
|         if p2_entry.is_unused() { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         PhysFrame::from_start_address(p2_entry.addr()).ok() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a> Mapper<Size4KiB> for RecursivePageTable<'a> { | ||||
|     fn map_to<A>( | ||||
|         &mut self, | ||||
|         page: Page<Size4KiB>, | ||||
|         frame: PhysFrame<Size4KiB>, | ||||
|         flags: PageTableFlags, | ||||
|         allocator: &mut A, | ||||
|     ) -> Result<MapperFlush<Size4KiB>, MapToError> | ||||
|     where | ||||
|         A: FrameAllocator<Size4KiB>, | ||||
|     { | ||||
|         let p4 = &mut self.p4; | ||||
| 
 | ||||
|         let p3_page = p3_page(page, self.recursive_index); | ||||
|         let p3 = unsafe { Self::create_next_table(&mut p4[page.p4_index()], p3_page, allocator)? }; | ||||
| 
 | ||||
|         let p2_page = p2_page(page, self.recursive_index); | ||||
|         let p2 = unsafe { Self::create_next_table(&mut p3[page.p3_index()], p2_page, allocator)? }; | ||||
| 
 | ||||
|         let p1_page = p1_page(page, self.recursive_index); | ||||
|         let p1 = unsafe { Self::create_next_table(&mut p2[page.p2_index()], p1_page, allocator)? }; | ||||
| 
 | ||||
|         if !p1[page.p1_index()].is_unused() { | ||||
|             return Err(MapToError::PageAlreadyMapped); | ||||
|         } | ||||
|         p1[page.p1_index()].set_frame(frame, flags); | ||||
| 
 | ||||
|         Ok(MapperFlush::new(page)) | ||||
|     } | ||||
| 
 | ||||
|     fn unmap( | ||||
|         &mut self, | ||||
|         page: Page<Size4KiB>, | ||||
|     ) -> Result<(PhysFrame<Size4KiB>, MapperFlush<Size4KiB>), UnmapError> { | ||||
|         let p4 = &mut self.p4; | ||||
|         let p4_entry = &p4[page.p4_index()]; | ||||
|         p4_entry.frame().map_err(|err| match err { | ||||
|             FrameError::FrameNotPresent => UnmapError::PageNotMapped, | ||||
|             FrameError::HugeFrame => UnmapError::ParentEntryHugePage, | ||||
|         })?; | ||||
| 
 | ||||
|         let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; | ||||
|         let p3_entry = &p3[page.p3_index()]; | ||||
|         p3_entry.frame().map_err(|err| match err { | ||||
|             FrameError::FrameNotPresent => UnmapError::PageNotMapped, | ||||
|             FrameError::HugeFrame => UnmapError::ParentEntryHugePage, | ||||
|         })?; | ||||
| 
 | ||||
|         let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; | ||||
|         let p2_entry = &p2[page.p2_index()]; | ||||
|         p2_entry.frame().map_err(|err| match err { | ||||
|             FrameError::FrameNotPresent => UnmapError::PageNotMapped, | ||||
|             FrameError::HugeFrame => UnmapError::ParentEntryHugePage, | ||||
|         })?; | ||||
| 
 | ||||
|         let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) }; | ||||
|         let p1_entry = &mut p1[page.p1_index()]; | ||||
| 
 | ||||
|         let frame = p1_entry.frame().map_err(|err| match err { | ||||
|             FrameError::FrameNotPresent => UnmapError::PageNotMapped, | ||||
|             FrameError::HugeFrame => UnmapError::ParentEntryHugePage, | ||||
|         })?; | ||||
| 
 | ||||
|         p1_entry.set_unused(); | ||||
|         Ok((frame, MapperFlush::new(page))) | ||||
|     } | ||||
| 
 | ||||
|     fn update_flags( | ||||
|         &mut self, | ||||
|         page: Page<Size4KiB>, | ||||
|         flags: PageTableFlags, | ||||
|     ) -> Result<MapperFlush<Size4KiB>, FlagUpdateError> { | ||||
|         let p4 = &mut self.p4; | ||||
| 
 | ||||
|         if p4[page.p4_index()].is_unused() { | ||||
|             return Err(FlagUpdateError::PageNotMapped); | ||||
|         } | ||||
| 
 | ||||
|         let p3 = unsafe { &mut *(p3_ptr(page, self.recursive_index)) }; | ||||
| 
 | ||||
|         if p3[page.p3_index()].is_unused() { | ||||
|             return Err(FlagUpdateError::PageNotMapped); | ||||
|         } | ||||
| 
 | ||||
|         let p2 = unsafe { &mut *(p2_ptr(page, self.recursive_index)) }; | ||||
| 
 | ||||
|         if p2[page.p2_index()].is_unused() { | ||||
|             return Err(FlagUpdateError::PageNotMapped); | ||||
|         } | ||||
| 
 | ||||
|         let p1 = unsafe { &mut *(p1_ptr(page, self.recursive_index)) }; | ||||
| 
 | ||||
|         if p1[page.p1_index()].is_unused() { | ||||
|             return Err(FlagUpdateError::PageNotMapped); | ||||
|         } | ||||
| 
 | ||||
|         p1[page.p1_index()].set_flags(flags); | ||||
| 
 | ||||
|         Ok(MapperFlush::new(page)) | ||||
|     } | ||||
| 
 | ||||
|     fn translate_page(&self, page: Page<Size4KiB>) -> Option<PhysFrame<Size4KiB>> { | ||||
|         let p4 = &self.p4; | ||||
| 
 | ||||
|         if p4[page.p4_index()].is_unused() { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         let p3 = unsafe { &*(p3_ptr(page, self.recursive_index)) }; | ||||
|         let p3_entry = &p3[page.p3_index()]; | ||||
| 
 | ||||
|         if p3_entry.is_unused() { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         let p2 = unsafe { &*(p2_ptr(page, self.recursive_index)) }; | ||||
|         let p2_entry = &p2[page.p2_index()]; | ||||
| 
 | ||||
|         if p2_entry.is_unused() { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         let p1 = unsafe { &*(p1_ptr(page, self.recursive_index)) }; | ||||
|         let p1_entry = &p1[page.p1_index()]; | ||||
| 
 | ||||
|         if p1_entry.is_unused() { | ||||
|             return None; | ||||
|         } | ||||
| 
 | ||||
|         PhysFrame::from_start_address(p1_entry.addr()).ok() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn p3_ptr<S: PageSize>(page: Page<S>, recursive_index: u9) -> *mut PageTable { | ||||
|     p3_page(page, recursive_index).start_address().as_mut_ptr() | ||||
| } | ||||
| 
 | ||||
| fn p3_page<S: PageSize>(page: Page<S>, recursive_index: u9) -> Page { | ||||
|     Page::from_page_table_indices( | ||||
|         recursive_index, | ||||
|         recursive_index, | ||||
|         recursive_index, | ||||
|         page.p4_index(), | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| fn p2_ptr<S: NotGiantPageSize>(page: Page<S>, recursive_index: u9) -> *mut PageTable { | ||||
|     p2_page(page, recursive_index).start_address().as_mut_ptr() | ||||
| } | ||||
| 
 | ||||
| fn p2_page<S: NotGiantPageSize>(page: Page<S>, recursive_index: u9) -> Page { | ||||
|     Page::from_page_table_indices( | ||||
|         recursive_index, | ||||
|         recursive_index, | ||||
|         page.p4_index(), | ||||
|         page.p3_index(), | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| fn p1_ptr(page: Page<Size4KiB>, recursive_index: u9) -> *mut PageTable { | ||||
|     p1_page(page, recursive_index).start_address().as_mut_ptr() | ||||
| } | ||||
| 
 | ||||
| fn p1_page(page: Page<Size4KiB>, recursive_index: u9) -> Page { | ||||
|     Page::from_page_table_indices( | ||||
|         recursive_index, | ||||
|         page.p4_index(), | ||||
|         page.p3_index(), | ||||
|         page.p2_index(), | ||||
|     ) | ||||
| } | ||||
| @ -0,0 +1,31 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Counter-timer Frequency register - EL0
 | ||||
| //!
 | ||||
| //! This register is provided so that software can discover the frequency of the
 | ||||
| //! system counter. It must be programmed with this value as part of system
 | ||||
| //! initialization. The value of the register is not interpreted by hardware.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadOnly; | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadOnly<u32, ()> for Reg { | ||||
|     sys_coproc_read_raw!(u32, "CNTFRQ_EL0"); | ||||
| } | ||||
| 
 | ||||
| pub static CNTFRQ_EL0: Reg = Reg {}; | ||||
| @ -0,0 +1,75 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Counter-timer Hypervisor Control register - EL2
 | ||||
| //!
 | ||||
| //! Controls the generation of an event stream from the physical counter, and
 | ||||
| //! access from Non-secure EL1 to the physical counter and the Non-secure EL1
 | ||||
| //! physical timer.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| // When HCR_EL2.E2H == 0:
 | ||||
| // TODO: Figure out how we can differentiate depending on HCR_EL2.E2H state
 | ||||
| //
 | ||||
| // For now, implement the HCR_EL2.E2H == 0 version
 | ||||
| register_bitfields! {u32, | ||||
|     CNTHCTL_EL2 [ | ||||
|         /// Traps Non-secure EL0 and EL1 accesses to the physical timer
 | ||||
|         /// registers to EL2.
 | ||||
|         ///
 | ||||
|         /// 0 From AArch64 state: Non-secure EL0 and EL1 accesses to the
 | ||||
|         ///   CNTP_CTL_EL0, CNTP_CVAL_EL0, and CNTP_TVAL_EL0 are trapped to EL2,
 | ||||
|         ///   unless it is trapped by CNTKCTL_EL1.EL0PTEN.
 | ||||
|         ///
 | ||||
|         ///   From AArch32 state: Non-secure EL0 and EL1 accesses to the
 | ||||
|         ///   CNTP_CTL, CNTP_CVAL, and CNTP_TVAL are trapped to EL2, unless it
 | ||||
|         ///   is trapped by CNTKCTL_EL1.EL0PTEN or CNTKCTL.PL0PTEN.
 | ||||
|         ///
 | ||||
|         /// 1 This control does not cause any instructions to be trapped.
 | ||||
|         ///
 | ||||
|         /// If EL3 is implemented and EL2 is not implemented, behavior is as if
 | ||||
|         /// this bit is 1 other than for the purpose of a direct read.
 | ||||
|         EL1PCEN  OFFSET(1) NUMBITS(1) [], | ||||
| 
 | ||||
|         /// Traps Non-secure EL0 and EL1 accesses to the physical counter
 | ||||
|         /// register to EL2.
 | ||||
|         ///
 | ||||
|         /// 0 From AArch64 state: Non-secure EL0 and EL1 accesses to the
 | ||||
|         ///   CNTPCT_EL0 are trapped to EL2, unless it is trapped by
 | ||||
|         ///   CNTKCTL_EL1.EL0PCTEN.
 | ||||
|         ///
 | ||||
|         ///   From AArch32 state: Non-secure EL0 and EL1 accesses to the CNTPCT
 | ||||
|         ///   are trapped to EL2, unless it is trapped by CNTKCTL_EL1.EL0PCTEN
 | ||||
|         ///   or CNTKCTL.PL0PCTEN.
 | ||||
|         ///
 | ||||
|         /// 1 This control does not cause any instructions to be trapped.
 | ||||
|         ///
 | ||||
|         /// If EL3 is implemented and EL2 is not implemented, behavior is as if
 | ||||
|         /// this bit is 1 other than for the purpose of a direct read.
 | ||||
|         EL1PCTEN OFFSET(0) NUMBITS(1) [] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u32, CNTHCTL_EL2::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u32, "CNTHCTL_EL2"); | ||||
|     sys_coproc_write_raw!(u32, "CNTHCTL_EL2"); | ||||
| } | ||||
| 
 | ||||
| #[allow(non_upper_case_globals)] | ||||
| pub static CNTHCTL_EL2: Reg = Reg {}; | ||||
| @ -0,0 +1,62 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Counter-timer Physical Timer Control register - EL0
 | ||||
| //!
 | ||||
| //! Control register for the EL1 physical timer.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| register_bitfields! {u32, | ||||
|     CNTP_CTL_EL0 [ | ||||
|         /// The status of the timer. This bit indicates whether the timer
 | ||||
|         /// condition is met:
 | ||||
|         ///
 | ||||
|         /// 0 Timer condition is not met.
 | ||||
|         /// 1 Timer condition is met.
 | ||||
|         ///
 | ||||
|         /// When the value of the ENABLE bit is 1, ISTATUS indicates whether the
 | ||||
|         /// timer condition is met. ISTATUS takes no account of the value of the
 | ||||
|         /// IMASK bit. If the value of ISTATUS is 1 and the value of IMASK is 0
 | ||||
|         /// then the timer interrupt is asserted.
 | ||||
|         ///
 | ||||
|         /// When the value of the ENABLE bit is 0, the ISTATUS field is UNKNOWN.
 | ||||
|         ///
 | ||||
|         /// This bit is read-only.
 | ||||
|         ISTATUS OFFSET(2) NUMBITS(1) [], | ||||
| 
 | ||||
|         /// Timer interrupt mask bit. Permitted values are:
 | ||||
|         ///
 | ||||
|         /// 0 Timer interrupt is not masked by the IMASK bit.
 | ||||
|         /// 1 Timer interrupt is masked by the IMASK bit.
 | ||||
|         IMASK   OFFSET(1) NUMBITS(1) [], | ||||
| 
 | ||||
|         /// Enables the timer. Permitted values are:
 | ||||
|         ///
 | ||||
|         /// 0 Timer disabled.
 | ||||
|         /// 1 Timer enabled.
 | ||||
|         ENABLE  OFFSET(0) NUMBITS(1) [] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u32, CNTP_CTL_EL0::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u32, "CNTP_CTL_EL0"); | ||||
|     sys_coproc_write_raw!(u32, "CNTP_CTL_EL0"); | ||||
| } | ||||
| 
 | ||||
| pub static CNTP_CTL_EL0: Reg = Reg {}; | ||||
| @ -0,0 +1,30 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Counter-timer Physical Timer TimerValue register - EL0
 | ||||
| //!
 | ||||
| //! Holds the timer value for the EL1 physical timer.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u32, ()> for Reg { | ||||
|     sys_coproc_read_raw!(u32, "CNTP_TVAL_EL0"); | ||||
|     sys_coproc_write_raw!(u32, "CNTP_TVAL_EL0"); | ||||
| } | ||||
| 
 | ||||
| pub static CNTP_TVAL_EL0: Reg = Reg {}; | ||||
| @ -0,0 +1,29 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Counter-timer Physical Count register - EL0
 | ||||
| //!
 | ||||
| //! Holds the 64-bit physical count value.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadOnly; | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadOnly<u64, ()> for Reg { | ||||
|     sys_coproc_read_raw!(u64, "CNTPCT_EL0"); | ||||
| } | ||||
| 
 | ||||
| pub static CNTPCT_EL0: Reg = Reg {}; | ||||
| @ -0,0 +1,32 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Counter-timer Virtual Offset register - EL2
 | ||||
| //!
 | ||||
| //! Holds the 64-bit virtual offset. This is the offset between the physical
 | ||||
| //! count value visible in CNTPCT_EL0 and the virtual count value visible in
 | ||||
| //! CNTVCT_EL0.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u64, ()> for Reg { | ||||
|     sys_coproc_read_raw!(u64, "CNTVOFF_EL2"); | ||||
|     sys_coproc_write_raw!(u64, "CNTVOFF_EL2"); | ||||
| } | ||||
| 
 | ||||
| pub static CNTVOFF_EL2: Reg = Reg {}; | ||||
| @ -0,0 +1,52 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Current Exception Level
 | ||||
| //!
 | ||||
| //! Holds the current Exception level.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadOnly; | ||||
| 
 | ||||
| register_bitfields! {u32, | ||||
|     CurrentEL [ | ||||
|         /// Current Exception level. Possible values of this field are:
 | ||||
|         ///
 | ||||
|         /// 00 EL0
 | ||||
|         /// 01 EL1
 | ||||
|         /// 10 EL2
 | ||||
|         /// 11 EL3
 | ||||
|         ///
 | ||||
|         /// When the HCR_EL2.NV bit is 1, Non-secure EL1 read accesses to the
 | ||||
|         /// CurrentEL register return the value of 0x2 in this field.
 | ||||
|         ///
 | ||||
|         /// This field resets to a value that is architecturally UNKNOWN.
 | ||||
|         EL OFFSET(2) NUMBITS(2) [ | ||||
|             EL0 = 0, | ||||
|             EL1 = 1, | ||||
|             EL2 = 2, | ||||
|             EL3 = 3 | ||||
|         ] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadOnly<u32, CurrentEL::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u32, "CurrentEL"); | ||||
| } | ||||
| 
 | ||||
| #[allow(non_upper_case_globals)] | ||||
| pub static CurrentEL: Reg = Reg {}; | ||||
| @ -0,0 +1,90 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Interrupt Mask Bits
 | ||||
| //!
 | ||||
| //! Allows access to the interrupt mask bits.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| register_bitfields! {u32, | ||||
|     DAIF [ | ||||
|         /// Process state D mask. The possible values of this bit are:
 | ||||
|         ///
 | ||||
|         /// 0 Watchpoint, Breakpoint, and Software Step exceptions targeted at
 | ||||
|         ///   the current Exception level are not masked.
 | ||||
|         ///
 | ||||
|         /// 1 Watchpoint, Breakpoint, and Software Step exceptions targeted at
 | ||||
|         ///   the current Exception level are masked.
 | ||||
|         ///
 | ||||
|         /// When the target Exception level of the debug exception is higher
 | ||||
|         /// than the current Exception level, the exception is not masked by
 | ||||
|         /// this bit.
 | ||||
|         ///
 | ||||
|         /// When this register has an architecturally-defined reset value, this
 | ||||
|         /// field resets to 1.
 | ||||
|         D OFFSET(9) NUMBITS(1) [ | ||||
|             Unmasked = 0, | ||||
|             Masked = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// SError interrupt mask bit. The possible values of this bit are:
 | ||||
|         ///
 | ||||
|         /// 0 Exception not masked.
 | ||||
|         /// 1 Exception masked.
 | ||||
|         ///
 | ||||
|         /// When this register has an architecturally-defined reset value, this
 | ||||
|         /// field resets to 1 .
 | ||||
|         A OFFSET(8) NUMBITS(1) [ | ||||
|             Unmasked = 0, | ||||
|             Masked = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// IRQ mask bit. The possible values of this bit are:
 | ||||
|         ///
 | ||||
|         /// 0 Exception not masked.
 | ||||
|         /// 1 Exception masked.
 | ||||
|         ///
 | ||||
|         /// When this register has an architecturally-defined reset value, this
 | ||||
|         /// field resets to 1 .
 | ||||
|         I OFFSET(7) NUMBITS(1) [ | ||||
|             Unmasked = 0, | ||||
|             Masked = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// FIQ mask bit. The possible values of this bit are:
 | ||||
|         ///
 | ||||
|         /// 0 Exception not masked.
 | ||||
|         /// 1 Exception masked.
 | ||||
|         ///
 | ||||
|         /// When this register has an architecturally-defined reset value, this
 | ||||
|         /// field resets to 1 .
 | ||||
|         F OFFSET(6) NUMBITS(1) [ | ||||
|             Unmasked = 0, | ||||
|             Masked = 1 | ||||
|         ] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u32, DAIF::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u32, "DAIF"); | ||||
|     sys_coproc_write_raw!(u32, "DAIF"); | ||||
| } | ||||
| 
 | ||||
| pub static DAIF: Reg = Reg {}; | ||||
| @ -0,0 +1,30 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Exception Link Register - EL2
 | ||||
| //!
 | ||||
| //! When taking an exception to EL2, holds the address to return to.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u64, ()> for Reg { | ||||
|     sys_coproc_read_raw!(u64, "ELR_EL2"); | ||||
|     sys_coproc_write_raw!(u64, "ELR_EL2"); | ||||
| } | ||||
| 
 | ||||
| pub static ELR_EL2: Reg = Reg {}; | ||||
| @ -0,0 +1,123 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Hypervisor Configuration Register - EL2
 | ||||
| //!
 | ||||
| //! Provides configuration controls for virtualization, including defining
 | ||||
| //! whether various Non-secure operations are trapped to EL2.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| register_bitfields! {u64, | ||||
|     HCR_EL2 [ | ||||
|         /// Execution state control for lower Exception levels:
 | ||||
|         ///
 | ||||
|         /// 0 Lower levels are all AArch32.
 | ||||
|         /// 1 The Execution state for EL1 is AArch64. The Execution state for
 | ||||
|         ///   EL0 is determined by the current value of PSTATE.nRW when
 | ||||
|         ///   executing at EL0.
 | ||||
|         ///
 | ||||
|         /// If all lower Exception levels cannot use AArch32 then this bit is
 | ||||
|         /// RAO/WI.
 | ||||
|         ///
 | ||||
|         /// In an implementation that includes EL3, when SCR_EL3.NS==0, the PE
 | ||||
|         /// behaves as if this bit has the same value as the SCR_EL3.RW bit for
 | ||||
|         /// all purposes other than a direct read or write access of HCR_EL2.
 | ||||
|         ///
 | ||||
|         /// The RW bit is permitted to be cached in a TLB.
 | ||||
|         ///
 | ||||
|         /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE}
 | ||||
|         /// is {1, 1}, this field behaves as 1 for all purposes other than a
 | ||||
|         /// direct read of the value of this bit.
 | ||||
|         RW   OFFSET(31) NUMBITS(1) [ | ||||
|             AllLowerELsAreAarch32 = 0, | ||||
|             EL1IsAarch64 = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// Default Cacheability.
 | ||||
|         ///
 | ||||
|         /// 0 This control has no effect on the Non-secure EL1&0 translation
 | ||||
|         ///   regime.
 | ||||
|         ///
 | ||||
|         /// 1 In Non-secure state:
 | ||||
|         ///     - When EL1 is using AArch64, the PE behaves as if the value of
 | ||||
|         ///       the SCTLR_EL1.M field is 0 for all purposes other than
 | ||||
|         ///       returning the value of a direct read of SCTLR_EL1.
 | ||||
|         ///
 | ||||
|         ///     - When EL1 is using AArch32, the PE behaves as if the value of
 | ||||
|         ///       the SCTLR.M field is 0 for all purposes other than returning
 | ||||
|         ///       the value of a direct read of SCTLR.
 | ||||
|         ///
 | ||||
|         ///     - The PE behaves as if the value of the HCR_EL2.VM field is 1
 | ||||
|         ///       for all purposes other than returning the value of a direct
 | ||||
|         ///       read of HCR_EL2.
 | ||||
|         ///
 | ||||
|         ///     - The memory type produced by stage 1 of the EL1&0 translation
 | ||||
|         ///       regime is Normal Non-Shareable, Inner Write-Back Read-Allocate
 | ||||
|         ///       Write-Allocate, Outer Write-Back Read-Allocate Write-Allocate.
 | ||||
|         ///
 | ||||
|         /// This field has no effect on the EL2, EL2&0, and EL3 translation
 | ||||
|         /// regimes.
 | ||||
|         ///
 | ||||
|         /// This field is permitted to be cached in a TLB.
 | ||||
|         ///
 | ||||
|         /// In an implementation that includes EL3, when the value of SCR_EL3.NS
 | ||||
|         /// is 0 the PE behaves as if this field is 0 for all purposes other
 | ||||
|         /// than a direct read or write access of HCR_EL2.
 | ||||
|         ///
 | ||||
|         /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE}
 | ||||
|         /// is {1, 1}, this field behaves as 0 for all purposes other than a
 | ||||
|         /// direct read of the value of this field.
 | ||||
|         DC   OFFSET(12) NUMBITS(1) [], | ||||
| 
 | ||||
|         /// Set/Way Invalidation Override. Causes Non-secure EL1 execution of
 | ||||
|         /// the data cache invalidate by set/way instructions to perform a data
 | ||||
|         /// cache clean and invalidate by set/way:
 | ||||
|         ///
 | ||||
|         /// 0 This control has no effect on the operation of data cache
 | ||||
|         ///   invalidate by set/way instructions.
 | ||||
|         ///
 | ||||
|         /// 1 Data cache invalidate by set/way instructions perform a data cache
 | ||||
|         ///   clean and invalidate by set/way.
 | ||||
|         ///
 | ||||
|         /// When the value of this bit is 1:
 | ||||
|         ///
 | ||||
|         /// AArch32: DCISW performs the same invalidation as a DCCISW
 | ||||
|         ///          instruction.
 | ||||
|         ///
 | ||||
|         /// AArch64: DC ISW performs the same invalidation as a DC CISW
 | ||||
|         ///          instruction.
 | ||||
|         ///
 | ||||
|         /// This bit can be implemented as RES 1.
 | ||||
|         ///
 | ||||
|         /// In an implementation that includes EL3, when the value of SCR_EL3.NS
 | ||||
|         /// is 0 the PE behaves as if this field is 0 for all purposes other
 | ||||
|         /// than a direct read or write access of HCR_EL2.
 | ||||
|         ///
 | ||||
|         /// When HCR_EL2.TGE is 1, the PE ignores the value of this field for
 | ||||
|         /// all purposes other than a direct read of this field.
 | ||||
|         SWIO OFFSET(1) NUMBITS(1) [] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u64, HCR_EL2::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u64, "HCR_EL2"); | ||||
|     sys_coproc_write_raw!(u64, "HCR_EL2"); | ||||
| } | ||||
| 
 | ||||
| pub static HCR_EL2: Reg = Reg {}; | ||||
| @ -0,0 +1,82 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! AArch64 Memory Model Feature Register 0 - EL1
 | ||||
| //!
 | ||||
| //! Provides information about the implemented memory model and memory
 | ||||
| //! management support in AArch64 state.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadOnly; | ||||
| 
 | ||||
| register_bitfields! {u64, | ||||
|     ID_AA64MMFR0_EL1 [ | ||||
|         /// Support for 4KiB memory translation granule size. Defined values
 | ||||
|         /// are:
 | ||||
|         ///
 | ||||
|         /// 0000 4KiB granule supported.
 | ||||
|         /// 1111 4KiB granule not supported.
 | ||||
|         ///
 | ||||
|         /// All other values are reserved.
 | ||||
|         TGran4  OFFSET(28) NUMBITS(4) [ | ||||
|             Supported = 0b0000, | ||||
|             NotSupported = 0b1111 | ||||
|         ], | ||||
| 
 | ||||
|         /// Support for 64KiB memory translation granule size. Defined values
 | ||||
|         /// are:
 | ||||
|         ///
 | ||||
|         /// 0000 64KiB granule supported.
 | ||||
|         /// 1111 64KiB granule not supported.
 | ||||
|         ///
 | ||||
|         /// All other values are reserved.
 | ||||
|         TGran64 OFFSET(24) NUMBITS(4) [ | ||||
|             Supported = 0b0000, | ||||
|             NotSupported = 0b1111 | ||||
|         ], | ||||
| 
 | ||||
|         /// Physical Address range supported. Defined values are:
 | ||||
|         ///
 | ||||
|         /// 0000 32 bits, 4GiB.
 | ||||
|         /// 0001 36 bits, 64GiB.
 | ||||
|         /// 0010 40 bits, 1TiB.
 | ||||
|         /// 0011 42 bits, 4TiB.
 | ||||
|         /// 0100 44 bits, 16TiB.
 | ||||
|         /// 0101 48 bits, 256TiB.
 | ||||
|         /// 0110 52 bits, 4PiB.
 | ||||
|         ///
 | ||||
|         /// All other values are reserved.
 | ||||
|         ///
 | ||||
|         /// The value 0110 is permitted only if the implementation includes
 | ||||
|         /// ARMv8.2-LPA, otherwise it is reserved.
 | ||||
|         PARange OFFSET(0) NUMBITS(4) [ | ||||
|             Bits_32 = 0b0000, | ||||
|             Bits_36 = 0b0001, | ||||
|             Bits_40 = 0b0010, | ||||
|             Bits_42 = 0b0011, | ||||
|             Bits_44 = 0b0100, | ||||
|             Bits_48 = 0b0101, | ||||
|             Bits_52 = 0b0110 | ||||
|         ] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadOnly<u64, ID_AA64MMFR0_EL1::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u64, "ID_AA64MMFR0_EL1"); | ||||
| } | ||||
| 
 | ||||
| pub static ID_AA64MMFR0_EL1: Reg = Reg {}; | ||||
| @ -0,0 +1,85 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| macro_rules! __read_raw { | ||||
|     ($width:ty, $asm_instr:tt, $asm_reg_name:tt) => { | ||||
|         /// Reads the raw bits of the CPU register.
 | ||||
|         #[inline] | ||||
|         fn get(&self) -> $width { | ||||
|             match () { | ||||
|                 #[cfg(target_arch = "aarch64")] | ||||
|                 () => { | ||||
|                     let reg; | ||||
|                     unsafe { | ||||
|                         asm!(concat!($asm_instr, " $0, ", $asm_reg_name) : "=r"(reg) ::: "volatile"); | ||||
|                     } | ||||
|                     reg | ||||
|                 } | ||||
| 
 | ||||
|                 #[cfg(not(target_arch = "aarch64"))] | ||||
|                 () => unimplemented!(), | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| macro_rules! __write_raw { | ||||
|     ($width:ty, $asm_instr:tt, $asm_reg_name:tt) => { | ||||
|         /// Writes raw bits to the CPU register.
 | ||||
|         #[cfg_attr(not(target_arch = "aarch64"), allow(unused_variables))] | ||||
|         #[inline] | ||||
|         fn set(&self, value: $width) { | ||||
|             match () { | ||||
|                 #[cfg(target_arch = "aarch64")] | ||||
|                 () => { | ||||
|                     unsafe { | ||||
|                         asm!(concat!($asm_instr, " ", $asm_reg_name, ", $0") :: "r"(value) :: "volatile") | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 #[cfg(not(target_arch = "aarch64"))] | ||||
|                 () => unimplemented!(), | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| /// Raw read from system coprocessor registers
 | ||||
| macro_rules! sys_coproc_read_raw { | ||||
|     ($width:ty, $asm_reg_name:tt) => { | ||||
|         __read_raw!($width, "mrs", $asm_reg_name); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| /// Raw write to system coprocessor registers
 | ||||
| macro_rules! sys_coproc_write_raw { | ||||
|     ($width:ty, $asm_reg_name:tt) => { | ||||
|         __write_raw!($width, "msr", $asm_reg_name); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| /// Raw read from (ordinary) registers
 | ||||
| macro_rules! read_raw { | ||||
|     ($width:ty, $asm_reg_name:tt) => { | ||||
|         __read_raw!($width, "mov", $asm_reg_name); | ||||
|     }; | ||||
| } | ||||
| /// Raw write to (ordinary) registers
 | ||||
| macro_rules! write_raw { | ||||
|     ($width:ty, $asm_reg_name:tt) => { | ||||
|         __write_raw!($width, "mov", $asm_reg_name); | ||||
|     }; | ||||
| } | ||||
| @ -0,0 +1,105 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Memory Attribute Indirection Register - EL1
 | ||||
| //!
 | ||||
| //! Provides the memory attribute encodings corresponding to the possible
 | ||||
| //! AttrIndx values in a Long-descriptor format translation table entry for
 | ||||
| //! stage 1 translations at EL1.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| register_bitfields! {u64, | ||||
|     MAIR_EL1 [ | ||||
|         // TODO: Macrofy this
 | ||||
| 
 | ||||
|         /// Attribute 7
 | ||||
|         Attr7_HIGH OFFSET(60) NUMBITS(4) [], | ||||
|         Attr7_LOW_DEVICE OFFSET(56) NUMBITS(4) [], | ||||
|         Attr7_LOW_MEMORY OFFSET(56) NUMBITS(4) [], | ||||
| 
 | ||||
|         /// Attribute 6
 | ||||
|         Attr6_HIGH OFFSET(52) NUMBITS(4) [], | ||||
|         Attr6_LOW_DEVICE OFFSET(48) NUMBITS(4) [], | ||||
|         Attr6_LOW_MEMORY OFFSET(48) NUMBITS(4) [], | ||||
| 
 | ||||
|         /// Attribute 5
 | ||||
|         Attr5_HIGH OFFSET(44) NUMBITS(4) [], | ||||
|         Attr5_LOW_DEVICE OFFSET(40) NUMBITS(4) [], | ||||
|         Attr5_LOW_MEMORY OFFSET(40) NUMBITS(4) [], | ||||
| 
 | ||||
|         /// Attribute 4
 | ||||
|         Attr4_HIGH OFFSET(36) NUMBITS(4) [], | ||||
|         Attr4_LOW_DEVICE OFFSET(32) NUMBITS(4) [], | ||||
|         Attr4_LOW_MEMORY OFFSET(32) NUMBITS(4) [], | ||||
| 
 | ||||
|         /// Attribute 3
 | ||||
|         Attr3_HIGH OFFSET(28) NUMBITS(4) [], | ||||
|         Attr3_LOW_DEVICE OFFSET(24) NUMBITS(4) [], | ||||
|         Attr3_LOW_MEMORY OFFSET(24) NUMBITS(4) [], | ||||
| 
 | ||||
|         /// Attribute 2
 | ||||
|         Attr2_HIGH OFFSET(20) NUMBITS(4) [ | ||||
|             Device = 0b0000, | ||||
|             Memory_OuterNonCacheable = 0b0100, | ||||
|             Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 | ||||
|         ], | ||||
|         Attr2_LOW_DEVICE OFFSET(16) NUMBITS(4) [ | ||||
|             Device_nGnRE = 0b0100 | ||||
|         ], | ||||
|         Attr2_LOW_MEMORY OFFSET(16) NUMBITS(4) [ | ||||
|             InnerNonCacheable = 0b0100, | ||||
|             InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 | ||||
|         ], | ||||
| 
 | ||||
|         /// Attribute 1
 | ||||
|         Attr1_HIGH OFFSET(12) NUMBITS(4) [ | ||||
|             Device = 0b0000, | ||||
|             Memory_OuterNonCacheable = 0b0100, | ||||
|             Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 | ||||
|         ], | ||||
|         Attr1_LOW_DEVICE OFFSET(8) NUMBITS(4) [ | ||||
|             Device_nGnRE = 0b0100 | ||||
|         ], | ||||
|         Attr1_LOW_MEMORY OFFSET(8) NUMBITS(4) [ | ||||
|             InnerNonCacheable = 0b0100, | ||||
|             InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 | ||||
|         ], | ||||
| 
 | ||||
|         /// Attribute 0
 | ||||
|         Attr0_HIGH OFFSET(4) NUMBITS(4) [ | ||||
|             Device = 0b0000, | ||||
|             Memory_OuterNonCacheable = 0b0100, | ||||
|             Memory_OuterWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 | ||||
|         ], | ||||
|         Attr0_LOW_DEVICE OFFSET(0) NUMBITS(4) [ | ||||
|             Device_nGnRE = 0b0100 | ||||
|         ], | ||||
|         Attr0_LOW_MEMORY OFFSET(0) NUMBITS(4) [ | ||||
|             InnerNonCacheable = 0b0100, | ||||
|             InnerWriteBack_NonTransient_ReadAlloc_WriteAlloc = 0b1111 | ||||
|         ] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u64, MAIR_EL1::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u64, "MAIR_EL1"); | ||||
|     sys_coproc_write_raw!(u64, "MAIR_EL1"); | ||||
| } | ||||
| 
 | ||||
| pub static MAIR_EL1: Reg = Reg {}; | ||||
| @ -0,0 +1,51 @@ | ||||
| //! Processor core registers
 | ||||
| 
 | ||||
| #[macro_use] | ||||
| mod macros; | ||||
| 
 | ||||
| mod cntfrq_el0; | ||||
| mod cnthctl_el2; | ||||
| mod cntp_ctl_el0; | ||||
| mod cntp_tval_el0; | ||||
| mod cntpct_el0; | ||||
| mod cntvoff_el2; | ||||
| mod currentel; | ||||
| mod daif; | ||||
| mod elr_el2; | ||||
| mod hcr_el2; | ||||
| mod id_aa64mmfr0_el1; | ||||
| mod mair_el1; | ||||
| mod mpidr_el1; | ||||
| mod sctlr_el1; | ||||
| mod sp; | ||||
| mod sp_el0; | ||||
| mod sp_el1; | ||||
| mod spsel; | ||||
| mod spsr_el2; | ||||
| mod tcr_el1; | ||||
| mod ttbr0_el1; | ||||
| 
 | ||||
| // Export only the R/W traits and the static reg definitions
 | ||||
| pub use register::cpu::*; | ||||
| 
 | ||||
| pub use self::cntfrq_el0::CNTFRQ_EL0; | ||||
| pub use self::cnthctl_el2::CNTHCTL_EL2; | ||||
| pub use self::cntp_ctl_el0::CNTP_CTL_EL0; | ||||
| pub use self::cntp_tval_el0::CNTP_TVAL_EL0; | ||||
| pub use self::cntpct_el0::CNTPCT_EL0; | ||||
| pub use self::cntvoff_el2::CNTVOFF_EL2; | ||||
| pub use self::currentel::CurrentEL; | ||||
| pub use self::daif::DAIF; | ||||
| pub use self::elr_el2::ELR_EL2; | ||||
| pub use self::hcr_el2::HCR_EL2; | ||||
| pub use self::id_aa64mmfr0_el1::ID_AA64MMFR0_EL1; | ||||
| pub use self::mair_el1::MAIR_EL1; | ||||
| pub use self::mpidr_el1::MPIDR_EL1; | ||||
| pub use self::sctlr_el1::SCTLR_EL1; | ||||
| pub use self::sp::SP; | ||||
| pub use self::sp_el0::SP_EL0; | ||||
| pub use self::sp_el1::SP_EL1; | ||||
| pub use self::spsel::SPSel; | ||||
| pub use self::spsr_el2::SPSR_EL2; | ||||
| pub use self::tcr_el1::TCR_EL1; | ||||
| pub use self::ttbr0_el1::TTBR0_EL1; | ||||
| @ -0,0 +1,30 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Multiprocessor Affinity Register - EL1
 | ||||
| //!
 | ||||
| //! In a multiprocessor system, provides an additional PE
 | ||||
| //! identification mechanism for scheduling purposes.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadOnly; | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadOnly<u64, ()> for Reg { | ||||
|     sys_coproc_read_raw!(u64, "MPIDR_EL1"); | ||||
| } | ||||
| 
 | ||||
| pub static MPIDR_EL1: Reg = Reg {}; | ||||
| @ -0,0 +1,103 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! System Control Register - EL1
 | ||||
| //!
 | ||||
| //! Provides top level control of the system, including its memory system, at
 | ||||
| //! EL1 and EL0.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| register_bitfields! {u32, | ||||
|     SCTLR_EL1 [ | ||||
|         /// Instruction access Cacheability control, for accesses at EL0 and
 | ||||
|         /// EL1:
 | ||||
|         ///
 | ||||
|         /// 0 All instruction access to Normal memory from EL0 and EL1 are
 | ||||
|         ///   Non-cacheable for all levels of instruction and unified cache.
 | ||||
|         ///
 | ||||
|         ///   If the value of SCTLR_EL1.M is 0, instruction accesses from stage
 | ||||
|         ///   1 of the EL1&0 translation regime are to Normal, Outer Shareable,
 | ||||
|         ///   Inner Non-cacheable, Outer Non-cacheable memory.
 | ||||
|         ///
 | ||||
|         /// 1 This control has no effect on the Cacheability of instruction
 | ||||
|         ///   access to Normal memory from EL0 and EL1.
 | ||||
|         ///
 | ||||
|         ///   If the value of SCTLR_EL1.M is 0, instruction accesses from stage
 | ||||
|         ///   1 of the EL1&0 translation regime are to Normal, Outer Shareable,
 | ||||
|         ///   Inner Write-Through, Outer Write-Through memory.
 | ||||
|         ///
 | ||||
|         /// When the value of the HCR_EL2.DC bit is 1, then instruction access
 | ||||
|         /// to Normal memory from EL0 and EL1 are Cacheable regardless of the
 | ||||
|         /// value of the SCTLR_EL1.I bit.
 | ||||
|         ///
 | ||||
|         /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE}
 | ||||
|         /// is {1, 1}, this bit has no effect on the PE.
 | ||||
|         ///
 | ||||
|         /// When this register has an architecturally-defined reset value, this
 | ||||
|         /// field resets to 0.
 | ||||
|         I OFFSET(12) NUMBITS(1) [ | ||||
|             NonCacheable = 0, | ||||
|             Cacheable = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// Cacheability control, for data accesses.
 | ||||
|         ///
 | ||||
|         /// 0 All data access to Normal memory from EL0 and EL1, and all Normal
 | ||||
|         ///   memory accesses to the EL1&0 stage 1 translation tables, are
 | ||||
|         ///   Non-cacheable for all levels of data and unified cache.
 | ||||
|         ///
 | ||||
|         /// 1 This control has no effect on the Cacheability of:
 | ||||
|         ///     - Data access to Normal memory from EL0 and EL1.
 | ||||
|         ///     - Normal memory accesses to the EL1&0 stage 1 translation
 | ||||
|         ///       tables.
 | ||||
|         ///
 | ||||
|         /// When the value of the HCR_EL2.DC bit is 1, the PE ignores
 | ||||
|         /// SCLTR.C. This means that Non-secure EL0 and Non-secure EL1 data
 | ||||
|         /// accesses to Normal memory are Cacheable.
 | ||||
|         ///
 | ||||
|         /// When ARMv8.1-VHE is implemented, and the value of HCR_EL2.{E2H, TGE}
 | ||||
|         /// is {1, 1}, this bit has no effect on the PE.
 | ||||
|         ///
 | ||||
|         /// When this register has an architecturally-defined reset value, this
 | ||||
|         /// field resets to 0.
 | ||||
|         C OFFSET(2) NUMBITS(1) [ | ||||
|             NonCacheable = 0, | ||||
|             Cacheable = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// MMU enable for EL1 and EL0 stage 1 address translation. Possible
 | ||||
|         /// values of this bit are:
 | ||||
|         ///
 | ||||
|         /// 0 EL1 and EL0 stage 1 address translation disabled.
 | ||||
|         ///   See the SCTLR_EL1.I field for the behavior of instruction accesses
 | ||||
|         ///   to Normal memory.
 | ||||
|         /// 1 EL1 and EL0 stage 1 address translation enabled.
 | ||||
|         M OFFSET(0) NUMBITS(1) [ | ||||
|             Disable = 0, | ||||
|             Enable = 1 | ||||
|         ] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u32, SCTLR_EL1::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u32, "SCTLR_EL1"); | ||||
|     sys_coproc_write_raw!(u32, "SCTLR_EL1"); | ||||
| } | ||||
| 
 | ||||
| pub static SCTLR_EL1: Reg = Reg {}; | ||||
| @ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! The stack pointer
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u64, ()> for Reg { | ||||
|     read_raw!(u64, "sp"); | ||||
|     write_raw!(u64, "sp"); | ||||
| } | ||||
| 
 | ||||
| pub static SP: Reg = Reg {}; | ||||
| @ -0,0 +1,31 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! The stack pointer - EL0
 | ||||
| //!
 | ||||
| //! Holds the stack pointer associated with EL0. At higher Exception levels,
 | ||||
| //! this is used as the current stack pointer when the value of SPSel.SP is 0.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u64, ()> for Reg { | ||||
|     sys_coproc_read_raw!(u64, "SP_EL0"); | ||||
|     sys_coproc_write_raw!(u64, "SP_EL0"); | ||||
| } | ||||
| 
 | ||||
| pub static SP_EL0: Reg = Reg {}; | ||||
| @ -0,0 +1,36 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! The stack pointer - EL1
 | ||||
| //!
 | ||||
| //! Holds the stack pointer associated with EL1. When executing at EL1, the
 | ||||
| //! value of SPSel.SP determines the current stack pointer:
 | ||||
| //!
 | ||||
| //! SPSel.SP | current stack pointer
 | ||||
| //! --------------------------------
 | ||||
| //! 0        | SP_EL0
 | ||||
| //! 1        | SP_EL1
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u64, ()> for Reg { | ||||
|     sys_coproc_read_raw!(u64, "SP_EL1"); | ||||
|     sys_coproc_write_raw!(u64, "SP_EL1"); | ||||
| } | ||||
| 
 | ||||
| pub static SP_EL1: Reg = Reg {}; | ||||
| @ -0,0 +1,48 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Stack Pointer Select
 | ||||
| //!
 | ||||
| //! Allows the Stack Pointer to be selected between SP_EL0 and SP_ELx.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| register_bitfields! {u32, | ||||
|     SPSel [ | ||||
|         /// Stack pointer to use. Possible values of this bit are:
 | ||||
|         ///
 | ||||
|         /// 0 Use SP_EL0 at all Exception levels.
 | ||||
|         /// 1 Use SP_ELx for Exception level ELx.
 | ||||
|         ///
 | ||||
|         /// When this register has an architecturally-defined reset value, this
 | ||||
|         /// field resets to 1.
 | ||||
|         SP OFFSET(0) NUMBITS(1) [ | ||||
|             EL0 = 0, | ||||
|             ELx = 1 | ||||
|         ] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u32, SPSel::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u32, "SPSEL"); | ||||
|     sys_coproc_write_raw!(u32, "SPSEL"); | ||||
| } | ||||
| 
 | ||||
| #[allow(non_upper_case_globals)] | ||||
| pub static SPSel: Reg = Reg {}; | ||||
| @ -0,0 +1,106 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Saved Program Status Register - EL2
 | ||||
| //!
 | ||||
| //! Holds the saved process state when an exception is taken to EL2.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| register_bitfields! {u32, | ||||
|     SPSR_EL2 [ | ||||
|         /// Process state D mask. The possible values of this bit are:
 | ||||
|         ///
 | ||||
|         /// 0 Watchpoint, Breakpoint, and Software Step exceptions targeted at
 | ||||
|         ///   the current Exception level are not masked.
 | ||||
|         ///
 | ||||
|         /// 1 Watchpoint, Breakpoint, and Software Step exceptions targeted at
 | ||||
|         ///   the current Exception level are masked.
 | ||||
|         ///
 | ||||
|         /// When the target Exception level of the debug exception is higher
 | ||||
|         /// than the current Exception level, the exception is not masked by
 | ||||
|         /// this bit.
 | ||||
|         D OFFSET(9) NUMBITS(1) [ | ||||
|             Unmasked = 0, | ||||
|             Masked = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// SError interrupt mask bit. The possible values of this bit are:
 | ||||
|         ///
 | ||||
|         /// 0 Exception not masked.
 | ||||
|         /// 1 Exception masked.
 | ||||
|         A OFFSET(8) NUMBITS(1) [ | ||||
|             Unmasked = 0, | ||||
|             Masked = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// IRQ mask bit. The possible values of this bit are:
 | ||||
|         ///
 | ||||
|         /// 0 Exception not masked.
 | ||||
|         /// 1 Exception masked.
 | ||||
|         I OFFSET(7) NUMBITS(1) [ | ||||
|             Unmasked = 0, | ||||
|             Masked = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// FIQ mask bit. The possible values of this bit are:
 | ||||
|         ///
 | ||||
|         /// 0 Exception not masked.
 | ||||
|         /// 1 Exception masked.
 | ||||
|         F OFFSET(6) NUMBITS(1) [ | ||||
|             Unmasked = 0, | ||||
|             Masked = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// AArch64 state (Exception level and selected SP) that an exception
 | ||||
|         /// was taken from. The possible values are:
 | ||||
|         ///
 | ||||
|         /// M[3:0] | State
 | ||||
|         /// --------------
 | ||||
|         /// 0b0000 | EL0t
 | ||||
|         /// 0b0100 | EL1t
 | ||||
|         /// 0b0101 | EL1h
 | ||||
|         /// 0b1000 | EL2t
 | ||||
|         /// 0b1001 | EL2h
 | ||||
|         ///
 | ||||
|         /// Other values are reserved, and returning to an Exception level that
 | ||||
|         /// is using AArch64 with a reserved value in this field is treated as
 | ||||
|         /// an illegal exception return.
 | ||||
|         ///
 | ||||
|         /// The bits in this field are interpreted as follows:
 | ||||
|         ///   - M[3:2] holds the Exception Level.
 | ||||
|         ///   - M[1] is unused and is RES 0 for all non-reserved values.
 | ||||
|         ///   - M[0] is used to select the SP:
 | ||||
|         ///     - 0 means the SP is always SP0.
 | ||||
|         ///     - 1 means the exception SP is determined by the EL.
 | ||||
|         M OFFSET(0) NUMBITS(4) [ | ||||
|             EL0t = 0b0000, | ||||
|             EL1t = 0b0100, | ||||
|             EL1h = 0b0101, | ||||
|             EL2t = 0b1000, | ||||
|             EL2h = 0b1001 | ||||
|         ] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u32, SPSR_EL2::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u32, "SPSR_EL2"); | ||||
|     sys_coproc_write_raw!(u32, "SPSR_EL2"); | ||||
| } | ||||
| 
 | ||||
| pub static SPSR_EL2: Reg = Reg {}; | ||||
| @ -0,0 +1,178 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Translation Control Register - EL1
 | ||||
| //!
 | ||||
| //! The control register for stage 1 of the EL1&0 translation regime.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| register_bitfields! {u64, | ||||
|     TCR_EL1 [ | ||||
|         /// Top Byte ignored - indicates whether the top byte of an address is
 | ||||
|         /// used for address match for the TTBR0_EL1 region, or ignored and used
 | ||||
|         /// for tagged addresses. Defined values are:
 | ||||
|         ///
 | ||||
|         /// 0 Top Byte used in the address calculation.
 | ||||
|         /// 1 Top Byte ignored in the address calculation.
 | ||||
|         TBI0  OFFSET(37) NUMBITS(1) [ | ||||
|             Used = 0, | ||||
|             Ignored = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// Intermediate Physical Address Size.
 | ||||
|         ///
 | ||||
|         /// 000 32 bits, 4GiB.
 | ||||
|         /// 001 36 bits, 64GiB.
 | ||||
|         /// 010 40 bits, 1TiB.
 | ||||
|         /// 011 42 bits, 4TiB.
 | ||||
|         /// 100 44 bits, 16TiB.
 | ||||
|         /// 101 48 bits, 256TiB.
 | ||||
|         /// 110 52 bits, 4PiB
 | ||||
|         ///
 | ||||
|         /// Other values are reserved.
 | ||||
|         ///
 | ||||
|         /// The reserved values behave in the same way as the 101 or 110
 | ||||
|         /// encoding, but software must not rely on this property as the
 | ||||
|         /// behavior of the reserved values might change in a future revision of
 | ||||
|         /// the architecture.
 | ||||
|         ///
 | ||||
|         /// The value 110 is permitted only if ARMv8.2-LPA is implemented and
 | ||||
|         /// the translation granule size is 64KiB.
 | ||||
|         ///
 | ||||
|         /// In an implementation that supports 52-bit PAs, if the value of this
 | ||||
|         /// field is not 110 , then bits[51:48] of every translation table base
 | ||||
|         /// address for the stage of translation controlled by TCR_EL1 are 0000
 | ||||
|         /// .
 | ||||
|         IPS   OFFSET(32) NUMBITS(3) [ | ||||
|             Bits_32 = 0b000, | ||||
|             Bits_36 = 0b001, | ||||
|             Bits_40 = 0b010, | ||||
|             Bits_42 = 0b011, | ||||
|             Bits_44 = 0b100, | ||||
|             Bits_48 = 0b101, | ||||
|             Bits_52 = 0b110 | ||||
|         ], | ||||
| 
 | ||||
|         /// Granule size for the TTBR0_EL1.
 | ||||
|         ///
 | ||||
|         /// 00 4KiB
 | ||||
|         /// 01 64KiB
 | ||||
|         /// 10 16KiB
 | ||||
|         ///
 | ||||
|         /// Other values are reserved.
 | ||||
|         ///
 | ||||
|         /// If the value is programmed to either a reserved value, or a size
 | ||||
|         /// that has not been implemented, then the hardware will treat the
 | ||||
|         /// field as if it has been programmed to an IMPLEMENTATION DEFINED
 | ||||
|         /// choice of the sizes that has been implemented for all purposes other
 | ||||
|         /// than the value read back from this register.
 | ||||
|         ///
 | ||||
|         /// It is IMPLEMENTATION DEFINED whether the value read back is the
 | ||||
|         /// value programmed or the value that corresponds to the size chosen.
 | ||||
|         TG0   OFFSET(14) NUMBITS(2) [ | ||||
|             KiB_4 = 0b00, | ||||
|             KiB_16 = 0b10, | ||||
|             KiB_64 = 0b01 | ||||
|         ], | ||||
| 
 | ||||
|         /// Shareability attribute for memory associated with translation table
 | ||||
|         /// walks using TTBR0_EL1.
 | ||||
|         ///
 | ||||
|         /// 00 Non-shareable
 | ||||
|         /// 10 Outer Shareable
 | ||||
|         /// 11 Inner Shareable
 | ||||
|         ///
 | ||||
|         /// Other values are reserved.
 | ||||
|         SH0   OFFSET(12) NUMBITS(2) [ | ||||
|             None = 0b00, | ||||
|             Outer = 0b10, | ||||
|             Inner = 0b11 | ||||
|         ], | ||||
| 
 | ||||
|         /// Outer cacheability attribute for memory associated with translation
 | ||||
|         /// table walks using TTBR0_EL1.
 | ||||
|         ///
 | ||||
|         /// 00 Normal memory, Outer Non-cacheable
 | ||||
|         ///
 | ||||
|         /// 01 Normal memory, Outer Write-Back Read-Allocate Write-Allocate
 | ||||
|         ///    Cacheable
 | ||||
|         ///
 | ||||
|         /// 10 Normal memory, Outer Write-Through Read-Allocate No
 | ||||
|         ///    Write-Allocate Cacheable
 | ||||
|         ///
 | ||||
|         /// 11 Normal memory, Outer Write-Back Read-Allocate No Write-Allocate
 | ||||
|         ///    Cacheable
 | ||||
|         ORGN0 OFFSET(10) NUMBITS(2) [ | ||||
|             NonCacheable = 0b00, | ||||
|             WriteBack_ReadAlloc_WriteAlloc_Cacheable = 0b01, | ||||
|             WriteThrough_ReadAlloc_NoWriteAlloc_Cacheable = 0b10, | ||||
|             WriteBack_ReadAlloc_NoWriteAlloc_Cacheable = 0b11 | ||||
|         ], | ||||
| 
 | ||||
|         /// Inner cacheability attribute for memory associated with translation
 | ||||
|         /// table walks using TTBR0_EL1.
 | ||||
|         ///
 | ||||
|         /// 00 Normal memory, Inner Non-cacheable
 | ||||
|         ///
 | ||||
|         /// 01 Normal memory, Inner Write-Back Read-Allocate Write-Allocate
 | ||||
|         ///    Cacheable
 | ||||
|         ///
 | ||||
|         /// 10 Normal memory, Inner Write-Through Read-Allocate No
 | ||||
|         ///    Write-Allocate Cacheable
 | ||||
|         ///
 | ||||
|         /// 11 Normal memory, Inner Write-Back Read-Allocate No Write-Allocate
 | ||||
|         ///    Cacheable
 | ||||
|         IRGN0 OFFSET(8) NUMBITS(2) [ | ||||
|             NonCacheable = 0b00, | ||||
|             WriteBack_ReadAlloc_WriteAlloc_Cacheable = 0b01, | ||||
|             WriteThrough_ReadAlloc_NoWriteAlloc_Cacheable = 0b10, | ||||
|             WriteBack_ReadAlloc_NoWriteAlloc_Cacheable = 0b11 | ||||
|         ], | ||||
| 
 | ||||
|         /// Translation table walk disable for translations using
 | ||||
|         /// TTBR0_EL1. This bit controls whether a translation table walk is
 | ||||
|         /// performed on a TLB miss, for an address that is translated using
 | ||||
|         /// TTBR0_EL1. The encoding of this bit is:
 | ||||
|         ///
 | ||||
|         /// 0 Perform translation table walks using TTBR0_EL1.
 | ||||
|         ///
 | ||||
|         /// 1 A TLB miss on an address that is translated using TTBR0_EL1
 | ||||
|         ///   generates a Translation fault. No translation table walk is
 | ||||
|         ///   performed.
 | ||||
|         EPD0  OFFSET(7) NUMBITS(1) [ | ||||
|             EnableTTBR0Walks = 0, | ||||
|             DisableTTBR0Walks = 1 | ||||
|         ], | ||||
| 
 | ||||
|         /// The size offset of the memory region addressed by TTBR0_EL1. The
 | ||||
|         /// region size is 2^(64-T0SZ) bytes.
 | ||||
|         ///
 | ||||
|         /// The maximum and minimum possible values for T0SZ depend on the level
 | ||||
|         /// of translation table and the memory translation granule size, as
 | ||||
|         /// described in the AArch64 Virtual Memory System Architecture chapter.
 | ||||
|         T0SZ  OFFSET(0) NUMBITS(6) [] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u64, TCR_EL1::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u64, "TCR_EL1"); | ||||
|     sys_coproc_write_raw!(u64, "TCR_EL1"); | ||||
| } | ||||
| 
 | ||||
| pub static TCR_EL1: Reg = Reg {}; | ||||
| @ -0,0 +1,56 @@ | ||||
| /* | ||||
|  * Copyright (c) 2018 by the author(s) | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Licensed under either of | ||||
|  *   - Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 | ||||
|  *   - MIT License (http://opensource.org/licenses/MIT)
 | ||||
|  * at your option. | ||||
|  * | ||||
|  * ============================================================================= | ||||
|  * | ||||
|  * Author(s): | ||||
|  *   - Andre Richter <andre.o.richter@gmail.com> | ||||
|  */ | ||||
| 
 | ||||
| //! Translation Table Base Register 0 - EL1
 | ||||
| //!
 | ||||
| //! Holds the base address of the translation table for the initial lookup for
 | ||||
| //! stage 1 of the translation of an address from the lower VA range in the
 | ||||
| //! EL1&0 translation regime, and other information for this translation regime.
 | ||||
| 
 | ||||
| use register::cpu::RegisterReadWrite; | ||||
| 
 | ||||
| register_bitfields! {u64, | ||||
|     TTBR0_EL1 [ | ||||
|         /// An ASID for the translation table base address. The TCR_EL1.A1 field
 | ||||
|         /// selects either TTBR0_EL1.ASID or TTBR1_EL1.ASID.
 | ||||
|         ///
 | ||||
|         /// If the implementation has only 8 bits of ASID, then the upper 8 bits
 | ||||
|         /// of this field are RES 0.
 | ||||
|         ASID  OFFSET(48) NUMBITS(16) [], | ||||
| 
 | ||||
|         /// Translation table base address
 | ||||
|         BADDR OFFSET(1) NUMBITS(47) [], | ||||
| 
 | ||||
|         /// Common not Private
 | ||||
|         CnP   OFFSET(0) NUMBITS(1) [] | ||||
|     ] | ||||
| } | ||||
| 
 | ||||
| pub struct Reg; | ||||
| 
 | ||||
| impl RegisterReadWrite<u64, TTBR0_EL1::Register> for Reg { | ||||
|     sys_coproc_read_raw!(u64, "TTBR0_EL1"); | ||||
|     sys_coproc_write_raw!(u64, "TTBR0_EL1"); | ||||
| } | ||||
| 
 | ||||
| impl Reg { | ||||
|     #[inline] | ||||
|     pub fn set_baddr(&self, addr: u64) { | ||||
|         self.write(TTBR0_EL1::BADDR.val(addr >> 1)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub static TTBR0_EL1: Reg = Reg {}; | ||||
					Loading…
					
					
				
		Reference in new issue