From a681957bceeffbf0fadbbd5d5cc28779e5b55271 Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Tue, 10 Oct 2023 12:53:12 +0900 Subject: [PATCH 01/12] Fix a bug in page table walking --- lib/vmsa/src/page_table.rs | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/vmsa/src/page_table.rs b/lib/vmsa/src/page_table.rs index eac4eb76f38e5..bad05fd719aa2 100644 --- a/lib/vmsa/src/page_table.rs +++ b/lib/vmsa/src/page_table.rs @@ -278,20 +278,33 @@ where } let index = E::index::(page.address().into()); - match self.entries[index].is_valid() { - true => { - if L::THIS_LEVEL < level { - // Need to go deeper (recursive) - match self.subtable::(page) { - Ok(subtable) => subtable.entry(page, level, no_valid_check, func), - Err(e) => Err(e), + if no_valid_check { + if L::THIS_LEVEL < level { + // Need to go deeper (recursive) + match self.subtable::(page) { + Ok(subtable) => subtable.entry(page, level, no_valid_check, func), + Err(_e) => Ok((None, L::THIS_LEVEL)), + } + } else { + // The page is either LargePage or HugePage + Ok((func(&mut self.entries[index])?, L::THIS_LEVEL)) + } + } else { + match self.entries[index].is_valid() { + true => { + if L::THIS_LEVEL < level { + // Need to go deeper (recursive) + match self.subtable::(page) { + Ok(subtable) => subtable.entry(page, level, no_valid_check, func), + Err(_e) => Ok((None, L::THIS_LEVEL)), + } + } else { + // The page is either LargePage or HugePage + Ok((func(&mut self.entries[index])?, L::THIS_LEVEL)) } - } else { - // The page is either LargePage or HugePage - Ok((func(&mut self.entries[index])?, L::THIS_LEVEL)) } + false => Err(Error::MmNoEntry), } - false => Err(Error::MmNoEntry), } } From 777d854802992d5338fc2cbedb63d6e8d01ab3b0 Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Tue, 10 Oct 2023 12:36:52 +0900 Subject: [PATCH 02/12] Add a simple page fault handling --- lib/armv9a/src/regs.rs | 10 ++++++ rmm/src/exception/trap.rs | 54 +++++++++++++++++++++++++++++- rmm/src/exception/trap/syndrome.rs | 18 ++++++++-- 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/lib/armv9a/src/regs.rs b/lib/armv9a/src/regs.rs index 82c8dac9db817..1d2069dc2fd0a 100644 --- a/lib/armv9a/src/regs.rs +++ b/lib/armv9a/src/regs.rs @@ -50,7 +50,17 @@ define_bits!( DFSC[5 - 0] ); +pub const ESR_EL2_EC_UNKNOWN: u64 = 0; +pub const ESR_EL2_EC_WFX: u64 = 1; +pub const ESR_EL2_EC_FPU: u64 = 7; +pub const ESR_EL2_EC_SVC: u64 = 21; +pub const ESR_EL2_EC_HVC: u64 = 22; +pub const ESR_EL2_EC_SMC: u64 = 23; +pub const ESR_EL2_EC_SYSREG: u64 = 24; +pub const ESR_EL2_EC_SVE: u64 = 25; +pub const ESR_EL2_EC_INST_ABORT: u64 = 32; pub const ESR_EL2_EC_DATA_ABORT: u64 = 36; +pub const ESR_EL2_EC_SERROR: u64 = 47; define_register!(SP); define_sys_register!(SP_EL0); diff --git a/rmm/src/exception/trap.rs b/rmm/src/exception/trap.rs index abccd530b2245..c3eb153485af7 100644 --- a/rmm/src/exception/trap.rs +++ b/rmm/src/exception/trap.rs @@ -2,10 +2,12 @@ mod frame; pub mod syndrome; use self::frame::TrapFrame; +use self::syndrome::Fault; use self::syndrome::Syndrome; use super::lower::synchronous; use crate::cpu; use crate::event::realmexit; +use crate::mm::translation::PageTable; use crate::realm::context::Context; use crate::realm::vcpu::VCPU; @@ -52,8 +54,58 @@ pub extern "C" fn handle_exception(info: Info, esr: u32, tf: &mut TrapFrame) { debug!("{:?}\nESR: {:X}\n{:#X?}", info, esr, tf); tf.elr += 4; //continue } + Syndrome::PCAlignmentFault => { + debug!("PCAlignmentFault"); + } + Syndrome::DataAbort(fault) => { + let far = unsafe { FAR_EL2.get() }; + debug!("Data Abort (higher), far:{:X}", far); + match fault { + Fault::AddressSize { level } => { + debug!("address size, level:{}", level); + } + Fault::Translation { level } => { + debug!("translation, level:{}, esr:{:X}", level, esr); + PageTable::get_ref().map(far as usize, true); + tf.elr += 4; //continue + } + Fault::AccessFlag { level } => { + debug!("access flag, level:{}", level); + } + Fault::Permission { level } => { + debug!("permission, level:{}", level); + } + Fault::Alignment => { + debug!("alignment"); + } + Fault::TLBConflict => { + debug!("tlb conflict"); + } + Fault::Other(_x) => { + debug!("other"); + } + } + } + Syndrome::InstructionAbort(v) => { + debug!("Instruction Abort (higher)"); + } + Syndrome::HVC => { + debug!("HVC"); + } + Syndrome::SMC => { + debug!("SMC"); + } + Syndrome::SysRegInst => { + debug!("SysRegInst"); + } + Syndrome::WFX => { + debug!("WFX"); + } + Syndrome::Other(v) => { + debug!("Other"); + } undefined => { - panic!("{:?} and {:?} on CPU {:?}", info, esr, cpu::id()); + panic!("{:?} and ESR: {:X} on CPU {:?}", info, esr, cpu::id()); } }, _ => { diff --git a/rmm/src/exception/trap/syndrome.rs b/rmm/src/exception/trap/syndrome.rs index 7c601cfa78362..5ea04381af40c 100644 --- a/rmm/src/exception/trap/syndrome.rs +++ b/rmm/src/exception/trap/syndrome.rs @@ -52,9 +52,23 @@ impl From for Syndrome { 0b01_0011 => Syndrome::SMC, 0b01_0111 => Syndrome::SMC, 0b01_1000 => Syndrome::SysRegInst, - 0b10_0000 | 0b10_0001 => Syndrome::InstructionAbort(Fault::from(origin)), + 0b10_0000 => { + debug!("Instruction Abort from a lower Exception level"); + Syndrome::InstructionAbort(Fault::from(origin)) + } + 0b10_0001 => { + debug!("Instruction Abort taken without a change in Exception level"); + Syndrome::InstructionAbort(Fault::from(origin)) + } 0b10_0010 => Syndrome::PCAlignmentFault, - 0b10_0100 | 0b10_0101 => Syndrome::DataAbort(Fault::from(origin)), + 0b10_0100 => { + debug!("Data Abort from a lower Exception level"); + Syndrome::DataAbort(Fault::from(origin)) + } + 0b10_0101 => { + debug!("Data Abort without a change in Exception level"); + Syndrome::DataAbort(Fault::from(origin)) + } 0b10_0110 => Syndrome::SPAlignmentFault, 0b11_1100 => Syndrome::Brk((origin & ESR_EL2::ISS_BRK_CMT as u32) as u16), ec => Syndrome::Other(ec as u32), From 277b0931e62055fbf5deaae36d4448caf1cbc72f Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Tue, 10 Oct 2023 15:33:41 +0900 Subject: [PATCH 03/12] Return a sub index in RmiErrorRtt --- rmm/src/realm/registry.rs | 38 +++++++++++++++++++++++++------------- rmm/src/rmi/error.rs | 4 ++-- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/rmm/src/realm/registry.rs b/rmm/src/realm/registry.rs index 59ffc0d05d9b0..5259c0f07bfda 100644 --- a/rmm/src/realm/registry.rs +++ b/rmm/src/realm/registry.rs @@ -459,7 +459,7 @@ impl crate::rmi::Interface for RMI { .page_table .lock() .ipa_to_pte(GuestPhysAddr::from(ipa), level) - .map_or((0, level), |pte| pte); + .ok_or(Error::RmiErrorRtt(0))?; let r1 = last_level; let (mut r2, mut r3, mut r4) = (0, 0, 0); @@ -474,11 +474,17 @@ impl crate::rmi::Interface for RMI { r2 = rtt_entry_state::RMI_DESTROYED; } else if s2tte.is_assigned() { r2 = rtt_entry_state::RMI_ASSIGNED; - r3 = s2tte.address(last_level).ok_or(Error::RmiErrorRtt)?.into(); + r3 = s2tte + .address(last_level) + .ok_or(Error::RmiErrorRtt(0))? + .into(); //XXX: check this again r4 = invalid_ripas::EMPTY as usize; } else if s2tte.is_valid(last_level, false) { r2 = rtt_entry_state::RMI_ASSIGNED; - r3 = s2tte.address(last_level).ok_or(Error::RmiErrorRtt)?.into(); + r3 = s2tte + .address(last_level) + .ok_or(Error::RmiErrorRtt(0))? + .into(); //XXX: check this again r4 = invalid_ripas::RAM as usize; } else if s2tte.is_valid(last_level, true) { r2 = rtt_entry_state::RMI_VALID_NS; @@ -487,14 +493,17 @@ impl crate::rmi::Interface for RMI { 2 => S2TTE::ADDR_L2_PAGE, 3 => S2TTE::ADDR_L3_PAGE, _ => { - return Err(Error::RmiErrorRtt); + return Err(Error::RmiErrorRtt(0)); //XXX: check this again } }; let mask = addr_mask | S2TTE::MEMATTR | S2TTE::AP | S2TTE::SH; r3 = (s2tte.get() & mask) as usize; } else if s2tte.is_table(last_level) { r2 = rtt_entry_state::RMI_TABLE; - r3 = s2tte.address(3).ok_or(Error::RmiErrorRtt)?.into(); + r3 = s2tte + .address(RTT_PAGE_LEVEL) + .ok_or(Error::RmiErrorRtt(0))? + .into(); //XXX: check this again } else { error!("Unexpected S2TTE value retrieved!"); } @@ -543,9 +552,9 @@ impl crate::rmi::Interface for RMI { .page_table .lock() .ipa_to_pte(GuestPhysAddr::from(ipa), level) - .ok_or(Error::RmiErrorRtt)?; + .ok_or(Error::RmiErrorRtt(0))?; //XXX: check this again if level != last_level { - return Err(Error::RmiErrorRtt); + return Err(Error::RmiErrorRtt(last_level)); //XXX: check this again } let s2tte = S2TTE::from(s2tte as usize); @@ -556,7 +565,7 @@ impl crate::rmi::Interface for RMI { // (rmm-spec) : Figure D2.1 Realm shared memory protocol flow if s2tte.is_valid(level, false) { // the case for ipa's range 0x8840_0000 - in realm-linux booting - let pa: usize = s2tte.address(level).ok_or(Error::RmiErrorRtt)?.into(); + let pa: usize = s2tte.address(level).ok_or(Error::RmiErrorRtt(0))?.into(); //XXX: check this again let size = crate::granule::GRANULE_SIZE; let mut flags = 0; flags |= bits_in_reg(S2TTE::INVALID_HIPAS, invalid_hipas::ASSIGNED); @@ -575,7 +584,7 @@ impl crate::rmi::Interface for RMI { true, )?; } else if s2tte.is_unassigned() || s2tte.is_assigned() { - let pa: usize = s2tte.address(level).ok_or(Error::RmiErrorRtt)?.into(); + let pa: usize = s2tte.address(level).ok_or(Error::RmiErrorRtt(0))?.into(); //XXX: check this again let size = crate::granule::GRANULE_SIZE; let mut flags = 0; flags |= bits_in_reg(S2TTE::INVALID_RIPAS, invalid_ripas::EMPTY); @@ -603,20 +612,23 @@ impl crate::rmi::Interface for RMI { .page_table .lock() .ipa_to_pte(GuestPhysAddr::from(ipa), 3) - .ok_or(Error::RmiErrorRtt)?; + .ok_or(Error::RmiErrorRtt(0))?; //XXX: check this again if last_level != 3 { - return Err(Error::RmiErrorRtt); + return Err(Error::RmiErrorRtt(last_level)); } let s2tte = S2TTE::from(s2tte as usize); let valid = s2tte.is_valid(last_level, false); if !valid && !s2tte.is_assigned() { - return Err(Error::RmiErrorRtt); + return Err(Error::RmiErrorRtt(RTT_PAGE_LEVEL)); } - let pa = s2tte.address(last_level).ok_or(Error::RmiErrorRtt)?.into(); + let pa = s2tte + .address(last_level) + .ok_or(Error::RmiErrorRtt(0))? + .into(); //XXX: check this again let mut flags = 0 as u64; if valid { diff --git a/rmm/src/rmi/error.rs b/rmm/src/rmi/error.rs index e4a7a1662abcd..b41755c9d3148 100644 --- a/rmm/src/rmi/error.rs +++ b/rmm/src/rmi/error.rs @@ -3,7 +3,7 @@ pub enum Error { RmiErrorInput, RmiErrorRealm, RmiErrorRec, - RmiErrorRtt, + RmiErrorRtt(usize), RmiErrorInUse, RmiErrorCount, //// The below are our-defined errors not in TF-RMM @@ -22,7 +22,7 @@ impl From for usize { Error::RmiErrorInput => 1, Error::RmiErrorRealm => 2, Error::RmiErrorRec => 3, - Error::RmiErrorRtt => 4, + Error::RmiErrorRtt(level) => 4 | (level << 8), Error::RmiErrorInUse => 5, Error::RmiErrorCount => 6, Error::RmiErrorOthers(_) => 7, From 55c11b2b14d5c7d3ae627f829f40a9ca982fd366 Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Tue, 10 Oct 2023 12:39:25 +0900 Subject: [PATCH 04/12] Add ipa_to_pte_set for s2tte modification --- lib/armv9a/src/macro.rs | 5 +++++ lib/vmsa/src/page_table.rs | 1 + rmm/src/granule/entry.rs | 4 ++++ rmm/src/mm/page_table/entry.rs | 4 ++++ rmm/src/realm/mm/mod.rs | 2 ++ rmm/src/realm/mm/page_table/entry.rs | 4 ++++ rmm/src/realm/mm/stage2_translation.rs | 19 +++++++++++++++++++ 7 files changed, 39 insertions(+) diff --git a/lib/armv9a/src/macro.rs b/lib/armv9a/src/macro.rs index ed1c9192ee9e3..62cf680f76c23 100644 --- a/lib/armv9a/src/macro.rs +++ b/lib/armv9a/src/macro.rs @@ -137,6 +137,11 @@ macro_rules! define_bits { $name(data) } + #[inline(always)] + pub fn get_mut(&mut self) -> &mut Self { + self + } + #[inline(always)] pub fn get(&self) -> u64 { self.0 diff --git a/lib/vmsa/src/page_table.rs b/lib/vmsa/src/page_table.rs index bad05fd719aa2..1b140336ba1da 100644 --- a/lib/vmsa/src/page_table.rs +++ b/lib/vmsa/src/page_table.rs @@ -31,6 +31,7 @@ pub trait Entry { fn clear(&mut self); fn pte(&self) -> u64; + fn mut_pte(&mut self) -> &mut Self::Inner; fn address(&self, level: usize) -> Option; fn set(&mut self, addr: PhysAddr, flags: u64, is_raw: bool) -> Result<(), Error>; diff --git a/rmm/src/granule/entry.rs b/rmm/src/granule/entry.rs index 8998c4621bcb6..627d2698a7a49 100644 --- a/rmm/src/granule/entry.rs +++ b/rmm/src/granule/entry.rs @@ -231,6 +231,10 @@ impl page_table::Entry for Entry { todo!(); } + fn mut_pte(&mut self) -> &mut Self::Inner { + self.0.get_mut() + } + fn address(&self, _level: usize) -> Option { Some(PhysAddr::from(self.0.lock().addr())) } diff --git a/rmm/src/mm/page_table/entry.rs b/rmm/src/mm/page_table/entry.rs index b61eb41ecee2e..713d58f04354f 100644 --- a/rmm/src/mm/page_table/entry.rs +++ b/rmm/src/mm/page_table/entry.rs @@ -46,6 +46,10 @@ impl page_table::Entry for Entry { self.0.get() } + fn mut_pte(&mut self) -> &mut Self::Inner { + self.0.get_mut() + } + fn address(&self, level: usize) -> Option { match self.is_valid() { true => match self.0.get_masked_value(PTDesc::TYPE) { diff --git a/rmm/src/realm/mm/mod.rs b/rmm/src/realm/mm/mod.rs index d477f12bb9256..c57e8bf14d184 100644 --- a/rmm/src/realm/mm/mod.rs +++ b/rmm/src/realm/mm/mod.rs @@ -25,5 +25,7 @@ pub trait IPATranslation: Debug + Send + Sync { // TODO: remove mut fn ipa_to_pa(&mut self, guest: GuestPhysAddr, level: usize) -> Option; fn ipa_to_pte(&mut self, guest: GuestPhysAddr, level: usize) -> Option<(u64, usize)>; + fn ipa_to_pte_set(&mut self, guest: GuestPhysAddr, level: usize, val: u64) + -> Result<(), Error>; fn clean(&mut self); } diff --git a/rmm/src/realm/mm/page_table/entry.rs b/rmm/src/realm/mm/page_table/entry.rs index 1d2d8b3ec1174..f14b28df8a096 100644 --- a/rmm/src/realm/mm/page_table/entry.rs +++ b/rmm/src/realm/mm/page_table/entry.rs @@ -29,6 +29,10 @@ impl page_table::Entry for Entry { self.0.get() } + fn mut_pte(&mut self) -> &mut Self::Inner { + self.0.get_mut() + } + fn address(&self, level: usize) -> Option { match self.is_valid() { true => match self.0.get_masked_value(RawPTE::TYPE) { diff --git a/rmm/src/realm/mm/stage2_translation.rs b/rmm/src/realm/mm/stage2_translation.rs index 16795cbe2da7a..38174c793146f 100644 --- a/rmm/src/realm/mm/stage2_translation.rs +++ b/rmm/src/realm/mm/stage2_translation.rs @@ -185,6 +185,25 @@ impl<'a> IPATranslation for Stage2Translation<'a> { } } + fn ipa_to_pte_set( + &mut self, + guest: GuestPhysAddr, + level: usize, + val: u64, + ) -> Result<(), Error> { + let guest = Page::::including_address(guest); + let res = self.root_pgtlb.entry(guest, level, true, |entry| { + let pte = entry.mut_pte(); + *pte = RawPTE(val); + Ok(None) + }); + if let Ok(_x) = res { + return Ok(()); + } else { + return Err(Error::RmiErrorInput); + } + } + fn clean(&mut self) { if self.dirty { unsafe { From 473a764325184a6322bbab4e4ee40ed67e808ee2 Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Tue, 10 Oct 2023 15:58:32 +0900 Subject: [PATCH 05/12] Remove map and unmap in rmi interface --- rmm/src/realm/registry.rs | 61 --------------------------------------- rmm/src/rmi/mod.rs | 9 ------ 2 files changed, 70 deletions(-) diff --git a/rmm/src/realm/registry.rs b/rmm/src/realm/registry.rs index 5259c0f07bfda..e63b2265a7229 100644 --- a/rmm/src/realm/registry.rs +++ b/rmm/src/realm/registry.rs @@ -148,67 +148,6 @@ impl crate::rmi::Interface for RMI { Ok(ret) } - fn map( - &self, - id: usize, - guest: usize, - phys: usize, - size: usize, - prot: usize, - ) -> Result<(), Error> { - let mut flags = 0; - let prot = MapProt::new(prot); - - if prot.is_set(MapProt::NS_PAS) { - flags |= bits_in_reg(S2TTE::NS, 0b1); - } - - // TODO: define bit mask - flags |= bits_in_reg(S2TTE::AP, pte::permission::RW); - if prot.is_set(MapProt::DEVICE) { - flags |= bits_in_reg(S2TTE::MEMATTR, pte::attribute::DEVICE_NGNRE); - flags |= bits_in_reg(S2TTE::NS, 0b1); - } else { - flags |= bits_in_reg(S2TTE::MEMATTR, pte::attribute::NORMAL_FWB); - } - - get_realm(id) - .ok_or(Error::RmiErrorOthers(NotExistRealm))? - .lock() - .page_table - .lock() - .set_pages( - GuestPhysAddr::from(guest), - PhysAddr::from(phys), - size, - flags as usize, - false, - )?; - - Ok(()) - } - - fn unmap(&self, id: usize, guest: usize, size: usize) -> Result { - let pa = get_realm(id) - .ok_or(Error::RmiErrorOthers(NotExistRealm))? - .lock() - .page_table - .lock() - .ipa_to_pa(GuestPhysAddr::from(guest), 3) - .ok_or(Error::RmiErrorInput)?; - - get_realm(id) - .ok_or(Error::RmiErrorOthers(NotExistRealm))? - .lock() - .page_table - .lock() - .unset_pages(GuestPhysAddr::from(guest), size); - - //TODO change GPT to nonsecure - //TODO zeroize memory - Ok(pa.into()) - } - fn set_reg(&self, id: usize, vcpu: usize, register: usize, value: usize) -> Result<(), Error> { match register { 0..=30 => { diff --git a/rmm/src/rmi/mod.rs b/rmm/src/rmi/mod.rs index a3e3455a5dd6d..95f3efba5f86a 100644 --- a/rmm/src/rmi/mod.rs +++ b/rmm/src/rmi/mod.rs @@ -113,15 +113,6 @@ pub trait Interface { fn realm_config(&self, id: usize, config_ipa: usize) -> Result<(), Error>; fn remove(&self, id: usize) -> Result<(), Error>; fn run(&self, id: usize, vcpu: usize, incr_pc: usize) -> Result<[usize; 4], Error>; - fn map( - &self, - id: usize, - guest: usize, - phys: usize, - size: usize, - prot: usize, - ) -> Result<(), Error>; - fn unmap(&self, id: usize, guest: usize, size: usize) -> Result; fn rtt_read_entry(&self, id: usize, guest: usize, level: usize) -> Result<[usize; 4], Error>; fn rtt_map_unprotected( &self, From c2acf09e0e272f20dd7194cc27cc5285419fd385 Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Wed, 11 Oct 2023 12:12:27 +0900 Subject: [PATCH 06/12] Change the way stage 2 page table is managed This commit is the implementation of the previous discussion (https://github.com//discussions/180). The stage 2 page table will be constructed in the location where the Host has intended with the APIs at the right timing. --- lib/vmsa/src/page_table.rs | 10 + rmm/src/realm/mm/mod.rs | 9 - rmm/src/realm/mm/page_table/pte.rs | 3 + rmm/src/realm/mm/stage2_translation.rs | 86 +------ rmm/src/realm/mm/stage2_tte.rs | 47 +++- rmm/src/realm/registry.rs | 328 +++++++++++++++++++++---- rmm/src/rmi/constraint.rs | 8 + rmm/src/rmi/mod.rs | 24 +- rmm/src/rmi/realm/mod.rs | 15 +- rmm/src/rmi/realm/rd.rs | 14 +- rmm/src/rmi/rtt.rs | 80 +++--- 11 files changed, 446 insertions(+), 178 deletions(-) diff --git a/lib/vmsa/src/page_table.rs b/lib/vmsa/src/page_table.rs index 1b140336ba1da..75add33bd49c2 100644 --- a/lib/vmsa/src/page_table.rs +++ b/lib/vmsa/src/page_table.rs @@ -83,6 +83,7 @@ pub struct PageTable { } pub trait PageTableMethods { + fn new_with_base(base: usize) -> Result<*mut PageTable, Error>; fn new_with_align(size: usize, align: usize) -> Result<*mut PageTable, Error>; /// Sets multiple page table entries /// @@ -171,6 +172,15 @@ impl PageTableMethods Result<*mut PageTable, Error> { + let table = base as *mut PageTable; + unsafe { + let arr: [E; N] = core::array::from_fn(|_| E::new()); + (*table).entries = arr; + } + Ok(table) + } + fn set_pages( &mut self, guest: PageIter, diff --git a/rmm/src/realm/mm/mod.rs b/rmm/src/realm/mm/mod.rs index c57e8bf14d184..1fa105935d73f 100644 --- a/rmm/src/realm/mm/mod.rs +++ b/rmm/src/realm/mm/mod.rs @@ -13,15 +13,6 @@ use address::{GuestPhysAddr, PhysAddr}; pub trait IPATranslation: Debug + Send + Sync { fn get_base_address(&self) -> *const c_void; - fn set_pages( - &mut self, - guest: GuestPhysAddr, - phys: PhysAddr, - size: usize, - flags: usize, - is_raw: bool, - ) -> Result<(), Error>; - fn unset_pages(&mut self, guest: GuestPhysAddr, size: usize); // TODO: remove mut fn ipa_to_pa(&mut self, guest: GuestPhysAddr, level: usize) -> Option; fn ipa_to_pte(&mut self, guest: GuestPhysAddr, level: usize) -> Option<(u64, usize)>; diff --git a/rmm/src/realm/mm/page_table/pte.rs b/rmm/src/realm/mm/page_table/pte.rs index 04d41fb02b3c5..5974e02e70ffe 100644 --- a/rmm/src/realm/mm/page_table/pte.rs +++ b/rmm/src/realm/mm/page_table/pte.rs @@ -1,4 +1,7 @@ pub mod shareable { + pub const NON_SHAREABLE: u64 = 0b00; + pub const RESERVED: u64 = 0b01; + pub const OUTER: u64 = 0b10; pub const INNER: u64 = 0b11; } diff --git a/rmm/src/realm/mm/stage2_translation.rs b/rmm/src/realm/mm/stage2_translation.rs index 38174c793146f..e32888b298b84 100644 --- a/rmm/src/realm/mm/stage2_translation.rs +++ b/rmm/src/realm/mm/stage2_translation.rs @@ -1,12 +1,11 @@ use super::page::BasePageSize; -use super::page_table::{entry, L1Table}; +use super::page_table::{entry, L0Table}; use core::arch::asm; use core::ffi::c_void; use core::fmt; use crate::realm::mm::address::GuestPhysAddr; -use crate::realm::mm::page_table::pte; use crate::realm::mm::translation_granule_4k::RawPTE; use crate::realm::mm::IPATranslation; use crate::rmi::error::Error; @@ -30,29 +29,26 @@ define_bits!(TLBI_OP, NS[63 - 63], TTL[47 - 44], IPA[35 - 0]); pub struct Stage2Translation<'a> { // We will set the translation granule with 4KB. - // To reduce the level of page lookup, initial lookup will start from L1. - // We allocate two single page table initial lookup table, addresing up 1TB. root_pgtlb: &'a mut PageTable< GuestPhysAddr, - L1Table, + L0Table, entry::Entry, - { ::NUM_ENTRIES }, + { ::NUM_ENTRIES }, >, dirty: bool, } impl<'a> Stage2Translation<'a> { - pub fn new() -> Self { + pub fn new(rtt_base: usize) -> Self { let root_pgtlb = unsafe { &mut *PageTable::< GuestPhysAddr, - L1Table, + L0Table, entry::Entry, - { ::NUM_ENTRIES }, - >::new_with_align(NUM_ROOT_PAGE, ALIGN_ROOT_PAGE) + { ::NUM_ENTRIES }, + >::new_with_base(rtt_base) .unwrap() }; - fill_stage2_table(root_pgtlb); Self { root_pgtlb, @@ -63,6 +59,7 @@ impl<'a> Stage2Translation<'a> { // According to DDI0608A E1.2.1.11 Cache and TLB operations // 'TLBI IPAS2E1, Xt; DSB; TLBI VMALLE1' // or TLBI ALL or TLBI VMALLS1S2 + #[allow(unused)] fn tlb_flush_by_vmid_ipa(&mut self, guest_iter: PageIter) { for guest in guest_iter { let _level: u64 = S::MAP_TABLE_LEVEL as u64; @@ -88,53 +85,6 @@ impl<'a> IPATranslation for Stage2Translation<'a> { self.root_pgtlb as *const _ as *const c_void } - fn set_pages( - &mut self, - guest: GuestPhysAddr, - phys: PhysAddr, - size: usize, - flags: usize, - is_raw: bool, - ) -> Result<(), Error> { - let _guest = Page::::range_with_size(guest, size); - let _guest_copy = Page::::range_with_size(guest, size); - let phys = Page::::range_with_size(phys, size); - - if is_raw { - if self - .root_pgtlb - .set_pages(_guest, phys, flags as u64, is_raw) - .is_err() - { - warn!("set_pages error"); - return Err(Error::RmiErrorInput); - } - } else { - if self - .root_pgtlb - .set_pages( - _guest, - phys, - flags as u64 | BasePageSize::MAP_EXTRA_FLAG, - is_raw, - ) - .is_err() - { - warn!("set_pages error"); - return Err(Error::RmiErrorInput); - } - } - self.tlb_flush_by_vmid_ipa::(_guest_copy); - - //TODO Set dirty only if pages are updated, not added - self.dirty = true; - Ok(()) - } - - fn unset_pages(&mut self, _guest: GuestPhysAddr, _size: usize) { - //TODO implement - } - /// Retrieves Page Table Entry (PA) from Intermediate Physical Address (IPA) /// /// (input) @@ -236,23 +186,3 @@ impl<'a> Drop for Stage2Translation<'a> { self.root_pgtlb.drop(); } } - -fn fill_stage2_table( - root: &mut PageTable, -) { - let device_flags = bits_in_reg(RawPTE::ATTR, pte::attribute::DEVICE_NGNRE) - | bits_in_reg(RawPTE::S2AP, pte::permission::RW); - let uart_guest = Page::::range_with_size( - GuestPhysAddr::from(0x1c0a0000 as u64), - 1, - ); - let uart_phys = - Page::::range_with_size(PhysAddr::from(0x1c0a0000 as u64), 1); - - if root - .set_pages(uart_guest, uart_phys, device_flags as u64, false) - .is_err() - { - warn!("set_pages error"); - } -} diff --git a/rmm/src/realm/mm/stage2_tte.rs b/rmm/src/realm/mm/stage2_tte.rs index 2d27213aadb11..28528c32290ab 100644 --- a/rmm/src/realm/mm/stage2_tte.rs +++ b/rmm/src/realm/mm/stage2_tte.rs @@ -1,6 +1,11 @@ +use core::mem::size_of; use vmsa::address::PhysAddr; +use crate::granule::{GranuleState, GRANULE_SIZE}; use armv9a::{define_bitfield, define_bits, define_mask}; +use vmsa::guard::Content; + +pub const INVALID_UNPROTECTED: u64 = 0x0; pub mod invalid_hipas { pub const UNASSIGNED: u64 = 0b00; @@ -20,10 +25,33 @@ pub mod desc_type { pub const LX_INVALID: u64 = 0x0; } +#[repr(C)] +#[derive(Copy, Clone)] +pub struct RttPage([u64; GRANULE_SIZE / size_of::()]); + +impl Content for RttPage { + const FLAGS: u64 = GranuleState::RTT; +} + +impl RttPage { + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> Option<&u64> { + self.0.get(index) + } + + pub fn get_mut(&mut self, index: usize) -> Option<&mut u64> { + self.0.get_mut(index) + } +} + define_bits!( S2TTE, NS[55 - 55], XN[54 - 54], + ADDR_L0_PAGE[47 - 39], // XXX: check this again ADDR_L1_PAGE[47 - 30], // XXX: check this again ADDR_L2_PAGE[47 - 21], // XXX: check this again ADDR_L3_PAGE[47 - 12], // XXX: check this again @@ -54,7 +82,8 @@ impl S2TTE { } pub fn is_unassigned(self) -> bool { - self.get_masked_value(S2TTE::INVALID_HIPAS) == invalid_hipas::UNASSIGNED + self.get_masked_value(S2TTE::DESC_TYPE) == desc_type::LX_INVALID + && self.get_masked_value(S2TTE::INVALID_HIPAS) == invalid_hipas::UNASSIGNED } pub fn is_destroyed(self) -> bool { @@ -79,4 +108,20 @@ impl S2TTE { _ => None, } } + + pub fn get_ripas(self) -> u64 { + let desc_ripas = self.get_masked_value(S2TTE::INVALID_RIPAS); + + if (self.get_masked_value(S2TTE::DESC_TYPE) != desc_type::LX_INVALID) + && (desc_ripas != invalid_ripas::RAM) + { + panic!("invalid ripas"); + } + + if desc_ripas == invalid_ripas::EMPTY { + return invalid_ripas::EMPTY; + } else { + return invalid_ripas::RAM; + } + } } diff --git a/rmm/src/realm/registry.rs b/rmm/src/realm/registry.rs index e63b2265a7229..d46e1647b1fd2 100644 --- a/rmm/src/realm/registry.rs +++ b/rmm/src/realm/registry.rs @@ -1,19 +1,23 @@ -use crate::realm::mm::address::{GuestPhysAddr, PhysAddr}; +use crate::realm::mm::address::GuestPhysAddr; +use crate::realm::mm::page_table::pte::{permission, shareable}; use crate::realm::mm::IPATranslation; use crate::realm::vcpu::VCPU; use crate::realm::Realm; +use crate::rmi::realm::Rd; use crate::rmi::rec::run::{Run, REC_ENTRY_FLAG_EMUL_MMIO}; -use crate::rmi::MapProt; +use crate::rmi::rtt::RTT_PAGE_LEVEL; use crate::gic; use crate::gic::{GIC_FEATURES, ICH_HCR_EL2_EOI_COUNT_MASK, ICH_HCR_EL2_NS_MASK}; +use crate::granule::{set_granule, GranuleState}; +use crate::mm::translation::PageTable; use crate::realm; use crate::realm::config::RealmConfig; use crate::realm::context::Context; -use crate::realm::mm::page_table::pte; +use crate::realm::mm::page_table::pte::attribute; use crate::realm::mm::stage2_translation::Stage2Translation; -use crate::realm::mm::stage2_tte::S2TTE; use crate::realm::mm::stage2_tte::{desc_type, invalid_hipas, invalid_ripas}; +use crate::realm::mm::stage2_tte::{RttPage, INVALID_UNPROTECTED, S2TTE}; use crate::realm::timer; use crate::rmi::error::Error; use crate::rmi::error::InternalError::*; @@ -66,7 +70,7 @@ impl RMI { } impl crate::rmi::Interface for RMI { - fn create_realm(&self, vmid: u16) -> Result { + fn create_realm(&self, vmid: u16, rtt_base: usize) -> Result { let mut rms = RMS.lock(); for (_, realm) in &rms.1 { @@ -77,7 +81,7 @@ impl crate::rmi::Interface for RMI { let id = rms.0; let s2_table = Arc::new(Mutex::new( - Box::new(Stage2Translation::new()) as Box + Box::new(Stage2Translation::new(rtt_base)) as Box )); let realm = Realm::new(id, vmid, s2_table); @@ -391,6 +395,141 @@ impl crate::rmi::Interface for RMI { } } + fn rtt_create( + &self, + id: usize, + rtt_addr: usize, + ipa: usize, + level: usize, + ) -> Result<(), Error> { + let mut rtt_granule = get_granule_if!(rtt_addr, GranuleState::Delegated)?; + let s2tt = rtt_granule.content_mut::(); + + let (parent_s2tte, last_level) = get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte(GuestPhysAddr::from(ipa), level - 1) + .ok_or(Error::RmiErrorRtt(0))?; //XXX: check this again + + if last_level != level - 1 { + return Err(Error::RmiErrorRtt(last_level)); + } + + let parent_s2tte = S2TTE::from(parent_s2tte as usize); + if parent_s2tte.is_unassigned() { + let ripas = parent_s2tte.get_ripas(); + let mut new_s2tte = bits_in_reg(S2TTE::INVALID_HIPAS, invalid_hipas::UNASSIGNED); + if ripas == invalid_ripas::EMPTY { + new_s2tte |= bits_in_reg(S2TTE::INVALID_RIPAS, invalid_ripas::EMPTY); + } else if ripas == invalid_ripas::RAM { + new_s2tte |= bits_in_reg(S2TTE::INVALID_RIPAS, invalid_ripas::RAM); + } else { + panic!("Unexpected ripas:{}", ripas); + } + + let s2tt_len = s2tt.len(); + for i in 0..s2tt_len { + if let Some(elem) = s2tt.get_mut(i) { + *elem = new_s2tte; + } + } + } + //TODO: add different cases (destroyed, assigned, valid, etc) + + set_granule(&mut rtt_granule, GranuleState::RTT)?; + + let parent_s2tte = rtt_addr as u64 | bits_in_reg(S2TTE::DESC_TYPE, desc_type::L012_TABLE); + get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte_set(GuestPhysAddr::from(ipa), level - 1, parent_s2tte)?; + + // The below is added to avoid a fault regarding the RTT entry + PageTable::get_ref().map(rtt_addr, true); + + Ok(()) + } + + fn rtt_destroy(&self, rd: &Rd, rtt_addr: usize, ipa: usize, level: usize) -> Result<(), Error> { + let id = rd.id(); + let (parent_s2tte, last_level) = get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte(GuestPhysAddr::from(ipa), level - 1) + .ok_or(Error::RmiErrorRtt(0))?; //XXX: check this again + + if last_level != level - 1 { + return Err(Error::RmiErrorRtt(last_level)); + } + + let parent_s2tte = S2TTE::from(parent_s2tte as usize); + if !parent_s2tte.is_table(level - 1) { + return Err(Error::RmiErrorRtt(level - 1)); + } + + let pa_table = parent_s2tte + .address(RTT_PAGE_LEVEL) + .ok_or(Error::RmiErrorInput)? + .try_into() + .or(Err(Error::RmiErrorInput))?; + if rtt_addr != pa_table { + return Err(Error::RmiErrorInput); + } + + let mut g_rtt = get_granule_if!(rtt_addr, GranuleState::RTT)?; + + let parent_s2tte; + if rd.addr_in_par(ipa) { + parent_s2tte = bits_in_reg(S2TTE::INVALID_HIPAS, invalid_hipas::DESTROYED); + } else { + parent_s2tte = INVALID_UNPROTECTED; + } + + get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte_set(GuestPhysAddr::from(ipa), level - 1, parent_s2tte)?; + + set_granule(&mut g_rtt, GranuleState::Delegated)?; + Ok(()) + } + + fn rtt_init_ripas(&self, id: usize, ipa: usize, level: usize) -> Result<(), Error> { + let (s2tte, last_level) = get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte(GuestPhysAddr::from(ipa), level) + .ok_or(Error::RmiErrorRtt(0))?; //XXX: check this again + + if level != last_level { + return Err(Error::RmiErrorRtt(last_level)); + } + + //TODO: add checks for `is_table` and `is_unassigned` + + let mut new_s2tte = s2tte; + new_s2tte |= bits_in_reg(S2TTE::INVALID_RIPAS, invalid_ripas::RAM); + + get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte_set(GuestPhysAddr::from(ipa), level, new_s2tte)?; + + Ok(()) + } + fn rtt_read_entry(&self, id: usize, ipa: usize, level: usize) -> Result<[usize; 4], Error> { let (s2tte, last_level) = get_realm(id) .ok_or(Error::RmiErrorOthers(NotExistRealm))? @@ -451,35 +590,53 @@ impl crate::rmi::Interface for RMI { fn rtt_map_unprotected( &self, - id: usize, + rd: &Rd, ipa: usize, - _level: usize, + level: usize, host_s2tte: usize, ) -> Result<(), Error> { - let size = crate::granule::GRANULE_SIZE; + if rd.addr_in_par(ipa) { + return Err(Error::RmiErrorInput); + } + + let id = rd.id(); + let (s2tte, last_level) = get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte(GuestPhysAddr::from(ipa), level) + .ok_or(Error::RmiErrorRtt(0))?; //XXX: check this again + + if level != last_level { + return Err(Error::RmiErrorRtt(last_level)); + } + + let s2tte = S2TTE::from(s2tte as usize); + + if !s2tte.is_unassigned() { + return Err(Error::RmiErrorRtt(level)); + } + let mut new_s2tte = host_s2tte as u64; - new_s2tte |= bits_in_reg(S2TTE::NS, 1) - | bits_in_reg(S2TTE::XN, 1) - | bits_in_reg(S2TTE::AF, 1) - | bits_in_reg(S2TTE::MEMATTR, pte::attribute::NORMAL_FWB) - | bits_in_reg(S2TTE::AP, pte::permission::RW) - | bits_in_reg(S2TTE::SH, pte::shareable::INNER) - | bits_in_reg(S2TTE::DESC_TYPE, desc_type::L3_PAGE); - let s2tte = S2TTE::from(new_s2tte as usize); - let pa = s2tte.get_masked(S2TTE::ADDR_FULL); - let flags = s2tte.get_masked(S2TTE::PAGE_FLAGS); + if level == RTT_PAGE_LEVEL { + new_s2tte |= bits_in_reg(S2TTE::NS, 1) + | bits_in_reg(S2TTE::XN, 1) + | bits_in_reg(S2TTE::AF, 1) + | bits_in_reg(S2TTE::DESC_TYPE, desc_type::L3_PAGE); + } else { + new_s2tte |= bits_in_reg(S2TTE::NS, 1) + | bits_in_reg(S2TTE::XN, 1) + | bits_in_reg(S2TTE::AF, 1) + | bits_in_reg(S2TTE::DESC_TYPE, desc_type::L012_BLOCK); + } + get_realm(id) .ok_or(Error::RmiErrorOthers(NotExistRealm))? .lock() .page_table .lock() - .set_pages( - GuestPhysAddr::from(ipa), - PhysAddr::from(pa), - size, - flags as usize, - true, - )?; + .ipa_to_pte_set(GuestPhysAddr::from(ipa), level, new_s2tte)?; Ok(()) } @@ -505,55 +662,124 @@ impl crate::rmi::Interface for RMI { if s2tte.is_valid(level, false) { // the case for ipa's range 0x8840_0000 - in realm-linux booting let pa: usize = s2tte.address(level).ok_or(Error::RmiErrorRtt(0))?.into(); //XXX: check this again - let size = crate::granule::GRANULE_SIZE; let mut flags = 0; flags |= bits_in_reg(S2TTE::INVALID_HIPAS, invalid_hipas::ASSIGNED); flags |= bits_in_reg(S2TTE::INVALID_RIPAS, invalid_ripas::EMPTY); + let new_s2tte = pa as u64 | flags; get_realm(id) .ok_or(Error::RmiErrorOthers(NotExistRealm))? .lock() .page_table .lock() - .set_pages( - GuestPhysAddr::from(ipa), - PhysAddr::from(pa), - size, - flags as usize, - true, - )?; + .ipa_to_pte_set(GuestPhysAddr::from(ipa), level, new_s2tte)?; } else if s2tte.is_unassigned() || s2tte.is_assigned() { let pa: usize = s2tte.address(level).ok_or(Error::RmiErrorRtt(0))?.into(); //XXX: check this again - let size = crate::granule::GRANULE_SIZE; - let mut flags = 0; - flags |= bits_in_reg(S2TTE::INVALID_RIPAS, invalid_ripas::EMPTY); + let flags = bits_in_reg(S2TTE::INVALID_RIPAS, invalid_ripas::EMPTY); + let new_s2tte = pa as u64 | flags; + get_realm(id) .ok_or(Error::RmiErrorOthers(NotExistRealm))? .lock() .page_table .lock() - .set_pages( - GuestPhysAddr::from(ipa), - PhysAddr::from(pa), - size, - flags as usize, - true, - )?; + .ipa_to_pte_set(GuestPhysAddr::from(ipa), level, new_s2tte)?; } Ok(()) } + fn make_exclusive(&self, id: usize, ipa: usize, level: usize) -> Result<(), Error> { + let (s2tte, last_level) = get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte(GuestPhysAddr::from(ipa), level) + .ok_or(Error::RmiErrorRtt(0))?; //XXX: check this again + if level != last_level { + return Err(Error::RmiErrorRtt(last_level)); //XXX: check this again + } + + let s2tte = S2TTE::from(s2tte as usize); + + if s2tte.is_valid(level, false) { + // This condition is added with no-op for handling the `else` case + } else if s2tte.is_unassigned() || s2tte.is_assigned() { + let pa: usize = s2tte.address(level).ok_or(Error::RmiErrorRtt(0))?.into(); //XXX: check this again + let flags = bits_in_reg(S2TTE::INVALID_RIPAS, invalid_ripas::RAM); + let new_s2tte = pa as u64 | flags; + + get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte_set(GuestPhysAddr::from(ipa), level, new_s2tte)?; + } else { + return Err(Error::RmiErrorRtt(level)); //XXX: check this again + } + + Ok(()) + } + + fn data_create(&self, id: usize, ipa: usize, target_pa: usize) -> Result<(), Error> { + let level = RTT_PAGE_LEVEL; + let (s2tte, last_level) = get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte(GuestPhysAddr::from(ipa), level) + .ok_or(Error::RmiErrorRtt(0))?; //XXX: check this again + + if level != last_level { + return Err(Error::RmiErrorRtt(last_level)); + } + + let s2tte = S2TTE::from(s2tte as usize); + if !s2tte.is_unassigned() { + return Err(Error::RmiErrorRtt(RTT_PAGE_LEVEL)); + } + + let mut new_s2tte = target_pa as u64; + let ripas = s2tte.get_ripas(); + if ripas == invalid_ripas::EMPTY { + new_s2tte |= bits_in_reg(S2TTE::INVALID_HIPAS, invalid_hipas::ASSIGNED); + new_s2tte |= bits_in_reg(S2TTE::INVALID_RIPAS, invalid_ripas::EMPTY); + } else if ripas == invalid_ripas::RAM { + // S2TTE_PAGE : S2TTE_ATTRS | S2TTE_L3_PAGE + new_s2tte |= bits_in_reg(S2TTE::DESC_TYPE, desc_type::L3_PAGE); + // S2TTE_ATTRS : S2TTE_MEMATTR_FWB_NORMAL_WB | S2TTE_AP_RW | S2TTE_SH_IS | S2TTE_AF + new_s2tte |= bits_in_reg(S2TTE::MEMATTR, attribute::NORMAL_FWB); + new_s2tte |= bits_in_reg(S2TTE::AP, permission::RW); + new_s2tte |= bits_in_reg(S2TTE::SH, shareable::INNER); + new_s2tte |= bits_in_reg(S2TTE::AF, 1); + } else { + panic!("Unexpected ripas: {}", ripas); + } + + get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte_set(GuestPhysAddr::from(ipa), level, new_s2tte)?; + + Ok(()) + } + fn data_destroy(&self, id: usize, ipa: usize) -> Result { + let level = RTT_PAGE_LEVEL; let (s2tte, last_level) = get_realm(id) .ok_or(Error::RmiErrorOthers(NotExistRealm))? .lock() .page_table .lock() - .ipa_to_pte(GuestPhysAddr::from(ipa), 3) + .ipa_to_pte(GuestPhysAddr::from(ipa), level) .ok_or(Error::RmiErrorRtt(0))?; //XXX: check this again - if last_level != 3 { + if last_level != level { return Err(Error::RmiErrorRtt(last_level)); } @@ -576,19 +802,13 @@ impl crate::rmi::Interface for RMI { flags |= bits_in_reg(S2TTE::INVALID_HIPAS, invalid_hipas::UNASSIGNED); flags |= bits_in_reg(S2TTE::INVALID_RIPAS, invalid_ripas::EMPTY); } - let size = crate::granule::GRANULE_SIZE; + let new_s2tte = flags; get_realm(id) .ok_or(Error::RmiErrorOthers(NotExistRealm))? .lock() .page_table .lock() - .set_pages( - GuestPhysAddr::from(ipa), - PhysAddr::from(0 as usize), - size, - flags as usize, - true, - )?; + .ipa_to_pte_set(GuestPhysAddr::from(ipa), level, new_s2tte)?; Ok(pa) } diff --git a/rmm/src/rmi/constraint.rs b/rmm/src/rmi/constraint.rs index c35c8bb229e0b..acf9b6abf9cbd 100644 --- a/rmm/src/rmi/constraint.rs +++ b/rmm/src/rmi/constraint.rs @@ -71,6 +71,14 @@ lazy_static! { rmi::REC_AUX_COUNT, Constraint::new(rmi::REC_AUX_COUNT, 2, 2), ); + m.insert( + rmi::RTT_CREATE, + Constraint::new(rmi::RTT_CREATE, 5, 1), + ); + m.insert( + rmi::RTT_DESTROY, + Constraint::new(rmi::RTT_DESTROY, 5, 1), + ); m.insert( rmi::RTT_INIT_RIPAS, Constraint::new(rmi::RTT_INIT_RIPAS, 4, 2), diff --git a/rmm/src/rmi/mod.rs b/rmm/src/rmi/mod.rs index 95f3efba5f86a..d5f6d1e624963 100644 --- a/rmm/src/rmi/mod.rs +++ b/rmm/src/rmi/mod.rs @@ -9,6 +9,7 @@ pub mod version; use crate::define_interface; use crate::rmi::error::Error; +use crate::rmi::realm::Rd; use crate::rmi::rec::run::Run; define_interface! { @@ -25,6 +26,8 @@ define_interface! { REC_CREATE = 0xc400_015a, REC_DESTROY = 0xc400_015b, REC_ENTER = 0xc400_015c, + RTT_CREATE = 0xc400_015d, + RTT_DESTROY = 0xc400_015e, RTT_MAP_UNPROTECTED = 0xc400_015f, RTT_UNMAP_UNPROTECTED = 0xc400_0162, RTT_READ_ENTRY = 0xc400_0161, @@ -108,21 +111,38 @@ impl MapProt { pub trait Interface { // TODO: it would be better to leave only true RMI interface here // while moving others to another place (e.g., set_reg()) - fn create_realm(&self, vmid: u16) -> Result; + fn create_realm(&self, vmid: u16, rtt_base: usize) -> Result; fn create_vcpu(&self, id: usize) -> Result; fn realm_config(&self, id: usize, config_ipa: usize) -> Result<(), Error>; fn remove(&self, id: usize) -> Result<(), Error>; fn run(&self, id: usize, vcpu: usize, incr_pc: usize) -> Result<[usize; 4], Error>; + fn rtt_create( + &self, + id: usize, + rtt_addr: usize, + guest: usize, + level: usize, + ) -> Result<(), Error>; + fn rtt_destroy( + &self, + rd: &Rd, + rtt_addr: usize, + guest: usize, + level: usize, + ) -> Result<(), Error>; + fn rtt_init_ripas(&self, id: usize, guest: usize, level: usize) -> Result<(), Error>; fn rtt_read_entry(&self, id: usize, guest: usize, level: usize) -> Result<[usize; 4], Error>; fn rtt_map_unprotected( &self, - id: usize, + rd: &Rd, guest: usize, level: usize, host_s2tte: usize, ) -> Result<(), Error>; + fn data_create(&self, id: usize, guest: usize, target_pa: usize) -> Result<(), Error>; fn data_destroy(&self, id: usize, guest: usize) -> Result; fn make_shared(&self, id: usize, guest: usize, level: usize) -> Result<(), Error>; + fn make_exclusive(&self, id: usize, guest: usize, level: usize) -> Result<(), Error>; fn set_reg(&self, id: usize, vcpu: usize, register: usize, value: usize) -> Result<(), Error>; fn get_reg(&self, id: usize, vcpu: usize, register: usize) -> Result; fn receive_gic_state_from_host(&self, id: usize, vcpu: usize, run: &Run) -> Result<(), Error>; diff --git a/rmm/src/rmi/realm/mod.rs b/rmm/src/rmi/realm/mod.rs index e123a93ecd355..202ecbeac5232 100644 --- a/rmm/src/rmi/realm/mod.rs +++ b/rmm/src/rmi/realm/mod.rs @@ -10,6 +10,7 @@ use crate::event::Mainloop; use crate::granule::{set_granule, GranuleState}; use crate::host::pointer::Pointer as HostPointer; use crate::listen; +use crate::mm::translation::PageTable; use crate::rmi; use crate::{get_granule, get_granule_if}; @@ -49,11 +50,21 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { // revisit rmi.create_realm() (is it necessary?) rmm.rmi - .create_realm(params.vmid) - .map(|id| rd_obj.init(id, params.rtt_base as usize, params.ipa_bits()))?; + .create_realm(params.vmid, params.rtt_base as usize) + .map(|id| { + rd_obj.init( + id, + params.rtt_base as usize, + params.ipa_bits(), + params.rtt_level_start as isize, + ) + })?; let id = rd_obj.id(); let rtt_base = rd_obj.rtt_base(); + // The below is added to avoid a fault regarding the RTT entry + PageTable::get_ref().map(rtt_base, true); + let mut eplilog = move || { let mut rtt_granule = get_granule_if!(rtt_base, GranuleState::Delegated)?; set_granule(&mut rtt_granule, GranuleState::RTT)?; diff --git a/rmm/src/rmi/realm/rd.rs b/rmm/src/rmi/realm/rd.rs index 32662521e8849..7cc2142bf0c55 100644 --- a/rmm/src/rmi/realm/rd.rs +++ b/rmm/src/rmi/realm/rd.rs @@ -1,4 +1,5 @@ use crate::granule::GranuleState; +use crate::rmi::rtt::realm_par_size; use vmsa::guard::Content; @@ -9,15 +10,17 @@ pub struct Rd { rtt_base: usize, ipa_bits: usize, rec_index: usize, + s2_starting_level: isize, } impl Rd { - pub fn init(&mut self, id: usize, rtt_base: usize, ipa_bits: usize) { + pub fn init(&mut self, id: usize, rtt_base: usize, ipa_bits: usize, s2_starting_level: isize) { self.realm_id = id; self.state = State::New; self.rtt_base = rtt_base; self.ipa_bits = ipa_bits; self.rec_index = 0; + self.s2_starting_level = s2_starting_level; } pub fn init_with_state(&mut self, id: usize, state: State) { @@ -53,9 +56,18 @@ impl Rd { self.rec_index } + pub fn s2_starting_level(&self) -> isize { + self.s2_starting_level + } + pub fn inc_rec_index(&mut self) { self.rec_index += 1; } + + pub fn addr_in_par(&self, addr: usize) -> bool { + let ipa_bits = self.ipa_bits(); + addr < realm_par_size(ipa_bits) + } } impl Content for Rd { diff --git a/rmm/src/rmi/rtt.rs b/rmm/src/rmi/rtt.rs index fd625261b2b9c..9459d4ea6d185 100644 --- a/rmm/src/rmi/rtt.rs +++ b/rmm/src/rmi/rtt.rs @@ -11,6 +11,8 @@ use crate::rmi; use crate::rmi::error::Error; use crate::{get_granule, get_granule_if, set_state_and_get_granule}; +pub const RTT_PAGE_LEVEL: usize = 3; + const RIPAS_EMPTY: u64 = 0; const RIPAS_RAM: u64 = 1; @@ -26,8 +28,41 @@ fn level_to_size(level: usize) -> u64 { } pub fn set_event_handler(mainloop: &mut Mainloop) { - listen!(mainloop, rmi::RTT_INIT_RIPAS, |_, _, _| { - super::dummy(); + listen!(mainloop, rmi::RTT_CREATE, |arg, _ret, rmm| { + let rmi = rmm.rmi; + let rtt_addr = arg[0]; + let rd_granule = get_granule_if!(arg[1], GranuleState::RD)?; + let rd = rd_granule.content::(); + let ipa = arg[2]; + let level = arg[3]; + + rmi.rtt_create(rd.id(), rtt_addr, ipa, level)?; + Ok(()) + }); + + listen!(mainloop, rmi::RTT_DESTROY, |arg, _ret, rmm| { + let rmi = rmm.rmi; + let rtt_addr = arg[0]; + let rd_granule = get_granule_if!(arg[1], GranuleState::RD)?; + let rd = rd_granule.content::(); + let ipa = arg[2]; + let level = arg[3]; + + rmi.rtt_destroy(rd, rtt_addr, ipa, level)?; + Ok(()) + }); + + listen!(mainloop, rmi::RTT_INIT_RIPAS, |arg, _ret, rmm| { + let rmi = rmm.rmi; + let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?; + let rd = rd_granule.content::(); + let ipa = arg[1]; + let level = arg[2]; + + if rd.state() != State::New { + return Err(Error::RmiErrorInput); + } + rmi.rtt_init_ripas(rd.id(), ipa, level)?; Ok(()) }); @@ -48,7 +83,7 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { RIPAS_RAM => { /* do nothing: ripas ram by default */ } _ => { warn!("Unknown RIPAS:{}", ripas); - return Err(Error::RmiErrorRtt); + return Err(Error::RmiErrorInput); //XXX: check this again } } @@ -66,10 +101,11 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { } if ripas as u64 == RIPAS_EMPTY { - let ret = rmi.make_shared(rd.id(), ipa, level); - if ret.is_err() { - return Err(Error::RmiErrorInput); - } + rmi.make_shared(rd.id(), ipa, level)?; + } else if ripas as u64 == RIPAS_RAM { + rmi.make_exclusive(rd.id(), ipa, level)?; + } else { + unreachable!(); } rec.inc_ripas_addr(map_size); Ok(()) @@ -129,19 +165,8 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { // 3. copy src to _data *target_page = src_page; - // 4. map ipa to _taget_pa into S2 table - let prot = rmi::MapProt::new(0); - let res = rmi.map( - realm_id, - ipa, - target_pa, - core::mem::size_of::(), - prot.get(), - ); - match res { - Ok(_) => {} - Err(val) => return Err(val), - } + // 4. map ipa to taget_pa in S2 table + rmi.data_create(realm_id, ipa, target_pa)?; // TODO: 5. perform measure set_granule(&mut target_page_granule, GranuleState::Data)?; @@ -158,20 +183,14 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { let rd_granule = get_granule_if!(arg[1], GranuleState::RD)?; let rd = rd_granule.content::(); let realm_id = rd.id(); - let granule_sz = GRANULE_SIZE; // 0. Make sure granule state can make a transition to DATA // data granule lock for the target page let mut target_page_granule = get_granule_if!(target_pa, GranuleState::Delegated)?; rmm.page_table.map(target_pa, true); - // 1. map ipa to target_pa into S2 table - let prot = rmi::MapProt::new(0); - let res = rmi.map(realm_id, ipa, target_pa, granule_sz, prot.get()); - match res { - Ok(_) => {} - Err(val) => return Err(val), - } + // 1. map ipa to target_pa in S2 table + rmi.data_create(realm_id, ipa, target_pa)?; // TODO: 2. perform measure set_granule(&mut target_page_granule, GranuleState::Data)?; @@ -200,11 +219,10 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { // rd granule lock let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?; let rd = rd_granule.content::(); - let realm_id = rd.id(); let level = arg[2]; let host_s2tte = arg[3]; - rmi.rtt_map_unprotected(realm_id, ipa, level, host_s2tte)?; + rmi.rtt_map_unprotected(rd, ipa, level, host_s2tte)?; Ok(()) }); @@ -224,7 +242,7 @@ fn realm_ipa_size(ipa_bits: usize) -> usize { 1 << ipa_bits } -fn realm_par_size(ipa_bits: usize) -> usize { +pub fn realm_par_size(ipa_bits: usize) -> usize { realm_ipa_size(ipa_bits) / 2 } From 19a502096789871ccd36417a8c95983ad9d1d989 Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Tue, 10 Oct 2023 17:16:04 +0900 Subject: [PATCH 07/12] Reimplement RMI_UNMAP_UNPROTECTED --- rmm/src/realm/registry.rs | 32 ++++++++++++++++++++++++++++++++ rmm/src/rmi/mod.rs | 1 + rmm/src/rmi/rtt.rs | 5 ++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/rmm/src/realm/registry.rs b/rmm/src/realm/registry.rs index d46e1647b1fd2..33329bfef7373 100644 --- a/rmm/src/realm/registry.rs +++ b/rmm/src/realm/registry.rs @@ -641,6 +641,38 @@ impl crate::rmi::Interface for RMI { Ok(()) } + fn rtt_unmap_unprotected(&self, id: usize, ipa: usize, level: usize) -> Result<(), Error> { + let (s2tte, last_level) = get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte(GuestPhysAddr::from(ipa), level) + .ok_or(Error::RmiErrorRtt(0))?; //XXX: check this again + + if level != last_level { + return Err(Error::RmiErrorRtt(last_level)); + } + + let s2tte = S2TTE::from(s2tte as usize); + if !s2tte.is_valid(level, true) { + return Err(Error::RmiErrorRtt(level)); + } + + let new_s2tte: u64 = INVALID_UNPROTECTED; + + get_realm(id) + .ok_or(Error::RmiErrorOthers(NotExistRealm))? + .lock() + .page_table + .lock() + .ipa_to_pte_set(GuestPhysAddr::from(ipa), level, new_s2tte)?; + + //TODO: add page/block invalidation + + Ok(()) + } + fn make_shared(&self, id: usize, ipa: usize, level: usize) -> Result<(), Error> { let (s2tte, last_level) = get_realm(id) .ok_or(Error::RmiErrorOthers(NotExistRealm))? diff --git a/rmm/src/rmi/mod.rs b/rmm/src/rmi/mod.rs index d5f6d1e624963..669e11aa1a1a3 100644 --- a/rmm/src/rmi/mod.rs +++ b/rmm/src/rmi/mod.rs @@ -139,6 +139,7 @@ pub trait Interface { level: usize, host_s2tte: usize, ) -> Result<(), Error>; + fn rtt_unmap_unprotected(&self, id: usize, guest: usize, level: usize) -> Result<(), Error>; fn data_create(&self, id: usize, guest: usize, target_pa: usize) -> Result<(), Error>; fn data_destroy(&self, id: usize, guest: usize) -> Result; fn make_shared(&self, id: usize, guest: usize, level: usize) -> Result<(), Error>; diff --git a/rmm/src/rmi/rtt.rs b/rmm/src/rmi/rtt.rs index 9459d4ea6d185..5088a77813ca4 100644 --- a/rmm/src/rmi/rtt.rs +++ b/rmm/src/rmi/rtt.rs @@ -233,7 +233,10 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { let rd_granule = get_granule_if!(arg[0], GranuleState::RD)?; let rd = rd_granule.content::(); - rmi.unmap(rd.id(), ipa, GRANULE_SIZE)?; + let realm_id = rd.id(); + + let level = arg[2]; + rmi.rtt_unmap_unprotected(realm_id, ipa, level)?; Ok(()) }); } From f27b9dddc335617b3588b62791e90e3e6f69ac02 Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Tue, 10 Oct 2023 16:51:18 +0900 Subject: [PATCH 08/12] Add a realm-linux feature to start walking from level 2 Realm-linux's stage 2 page table walking starts from level 2 instead of level 0. It also consists of 8 concatenated tables. This commit handles the table's difference with `realm-linux` feature. A refactoring work would be needed to remove this feature in the future. --- plat/fvp/Cargo.toml | 1 + rmm/Cargo.toml | 1 + rmm/src/realm/mm/page_table/entry.rs | 13 ++++++++++ rmm/src/realm/mm/page_table/mod.rs | 11 +++++++++ rmm/src/realm/mm/stage2_translation.rs | 34 ++++++++++++++++++++++++++ scripts/fvp-cca | 3 +++ scripts/tests/realm-boot.sh | 2 +- 7 files changed, 64 insertions(+), 1 deletion(-) diff --git a/plat/fvp/Cargo.toml b/plat/fvp/Cargo.toml index 901ca577a8a46..27299887b225d 100644 --- a/plat/fvp/Cargo.toml +++ b/plat/fvp/Cargo.toml @@ -16,6 +16,7 @@ max_level_info = ["log/max_level_info", "islet_rmm/max_level_info"] max_level_debug = ["log/max_level_debug", "islet_rmm/max_level_debug"] max_level_trace = ["log/max_level_trace", "islet_rmm/max_level_trace"] stat = ["islet_rmm/stat"] +realm_linux = ["islet_rmm/realm_linux"] [dependencies] armv9a = { path = "../../lib/armv9a" } diff --git a/rmm/Cargo.toml b/rmm/Cargo.toml index 51e09d3739b01..bf204d29d7348 100644 --- a/rmm/Cargo.toml +++ b/rmm/Cargo.toml @@ -25,4 +25,5 @@ max_level_warn = ["log/max_level_warn"] max_level_info = ["log/max_level_info"] max_level_debug = ["log/max_level_debug"] max_level_trace = ["log/max_level_trace"] +realm_linux = [] stat = [] diff --git a/rmm/src/realm/mm/page_table/entry.rs b/rmm/src/realm/mm/page_table/entry.rs index f14b28df8a096..ffc3b6377fe9b 100644 --- a/rmm/src/realm/mm/page_table/entry.rs +++ b/rmm/src/realm/mm/page_table/entry.rs @@ -1,4 +1,6 @@ use super::pte; +#[cfg(feature = "realm_linux")] +use crate::realm::mm::stage2_tte::S2TTE; use crate::realm::mm::translation_granule_4k::RawPTE; use vmsa::address::PhysAddr; use vmsa::error::Error; @@ -82,6 +84,17 @@ impl page_table::Entry for Entry { ) } + #[cfg(feature = "realm_linux")] + fn index(addr: usize) -> usize { + match L::THIS_LEVEL { + 0 => RawGPA::from(addr).get_masked_value(RawGPA::L0Index) as usize, + 1 => RawGPA::from(addr).get_masked_value(RawGPA::L1Index) as usize, + 2 => RawGPA::from(addr).get_masked_value(S2TTE::ADDR_L2_PAGE) as usize, + 3 => RawGPA::from(addr).get_masked_value(RawGPA::L3Index) as usize, + _ => panic!(), + } + } + #[cfg(not(feature = "realm_linux"))] fn index(addr: usize) -> usize { match L::THIS_LEVEL { 0 => RawGPA::from(addr).get_masked_value(RawGPA::L0Index) as usize, diff --git a/rmm/src/realm/mm/page_table/mod.rs b/rmm/src/realm/mm/page_table/mod.rs index 1569c382de5f6..e9277e4ce3e57 100644 --- a/rmm/src/realm/mm/page_table/mod.rs +++ b/rmm/src/realm/mm/page_table/mod.rs @@ -34,6 +34,14 @@ impl HasSubtable for L1Table { /// The Level 2 Table pub enum L2Table {} +#[cfg(feature = "realm_linux")] +impl Level for L2Table { + const THIS_LEVEL: usize = 2; + const TABLE_SIZE: usize = PAGE_SIZE * 8; // XXX: this is just for realm-linux + const TABLE_ALIGN: usize = PAGE_SIZE; + const NUM_ENTRIES: usize = (Self::TABLE_SIZE / core::mem::size_of::()); +} +#[cfg(not(feature = "realm_linux"))] impl Level for L2Table { const THIS_LEVEL: usize = 2; const TABLE_SIZE: usize = PAGE_SIZE; @@ -55,5 +63,8 @@ impl Level for L3Table { const_assert_size!(PageTable, PAGE_SIZE); const_assert_size!(PageTable, PAGE_SIZE); +#[cfg(feature = "realm_linux")] +const_assert_size!(PageTable, PAGE_SIZE * 8); +#[cfg(not(feature = "realm_linux"))] const_assert_size!(PageTable, PAGE_SIZE); const_assert_size!(PageTable, PAGE_SIZE); diff --git a/rmm/src/realm/mm/stage2_translation.rs b/rmm/src/realm/mm/stage2_translation.rs index e32888b298b84..789c50d2dfb5c 100644 --- a/rmm/src/realm/mm/stage2_translation.rs +++ b/rmm/src/realm/mm/stage2_translation.rs @@ -1,5 +1,8 @@ use super::page::BasePageSize; +#[cfg(not(feature = "realm_linux"))] use super::page_table::{entry, L0Table}; +#[cfg(feature = "realm_linux")] +use super::page_table::{entry, L2Table}; use core::arch::asm; use core::ffi::c_void; @@ -27,6 +30,18 @@ pub mod tlbi_ns { define_bits!(TLBI_OP, NS[63 - 63], TTL[47 - 44], IPA[35 - 0]); +#[cfg(feature = "realm_linux")] +pub struct Stage2Translation<'a> { + // We will set the translation granule with 4KB. + root_pgtlb: &'a mut PageTable< + GuestPhysAddr, + L2Table, + entry::Entry, + { ::NUM_ENTRIES }, + >, + dirty: bool, +} +#[cfg(not(feature = "realm_linux"))] pub struct Stage2Translation<'a> { // We will set the translation granule with 4KB. root_pgtlb: &'a mut PageTable< @@ -39,6 +54,25 @@ pub struct Stage2Translation<'a> { } impl<'a> Stage2Translation<'a> { + #[cfg(feature = "realm_linux")] + pub fn new(rtt_base: usize) -> Self { + let root_pgtlb = unsafe { + &mut *PageTable::< + GuestPhysAddr, + L2Table, + entry::Entry, + { ::NUM_ENTRIES }, + >::new_with_base(rtt_base) + .unwrap() + }; + + Self { + root_pgtlb, + dirty: false, + } + } + + #[cfg(not(feature = "realm_linux"))] pub fn new(rtt_base: usize) -> Self { let root_pgtlb = unsafe { &mut *PageTable::< diff --git a/scripts/fvp-cca b/scripts/fvp-cca index 488147e9f46e1..18f2a9b155455 100755 --- a/scripts/fvp-cca +++ b/scripts/fvp-cca @@ -143,6 +143,8 @@ def get_rmm_features(args): if args.stat == True: features += ["--features", "stat"] + if args.realm_linux == True: + features += ["--features", "realm_linux"] if features: print("[!] Setting", args.rmm, "features:", features) @@ -455,6 +457,7 @@ if __name__ == "__main__": parser.add_argument("--ifname", "-if", help="the main interface name of host machine", default="eth0") parser.add_argument("--rmm-log-level", help="Determine RMM's log-level. Choose among (off, error, warn, info, debug, trace)", default="trace") parser.add_argument("--stat", help="Enable stat to check memory used size per command", action="store_true") + parser.add_argument("--realm-linux", help="Enable realm-linux feature", action="store_true") args = parser.parse_args() diff --git a/scripts/tests/realm-boot.sh b/scripts/tests/realm-boot.sh index 310bb7869845c..977c8bbebaa66 100755 --- a/scripts/tests/realm-boot.sh +++ b/scripts/tests/realm-boot.sh @@ -26,7 +26,7 @@ check_result() } tar -xf $ROOT/assets/prebuilt/out.tar.bz2 -C $ROOT -$ROOT/scripts/fvp-cca -bo -rmm=islet --use-prebuilt --rmm-log-level=error +$ROOT/scripts/fvp-cca -bo -rmm=islet --use-prebuilt --rmm-log-level=error --realm-linux $ROOT/scripts/fvp-cca -ro -nw=linux --realm=linux -rmm=islet --no-telnet & sleep 640 From ecf4523a518aae2a905cd5ea0d85a7711eecd598 Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Tue, 10 Oct 2023 16:12:27 +0900 Subject: [PATCH 09/12] Make stage 2 activation configurable --- lib/armv9a/src/regs.rs | 59 +++++++++++++++++++++++++++++++++++-- rmm/src/lib.rs | 20 ------------- rmm/src/rmi/rec/handlers.rs | 4 +++ rmm/src/rmi/rec/mod.rs | 10 +++++++ rmm/src/rmi/rec/vtcr.rs | 55 ++++++++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 22 deletions(-) create mode 100644 rmm/src/rmi/rec/vtcr.rs diff --git a/lib/armv9a/src/regs.rs b/lib/armv9a/src/regs.rs index 1d2069dc2fd0a..16ed65447f90e 100644 --- a/lib/armv9a/src/regs.rs +++ b/lib/armv9a/src/regs.rs @@ -145,6 +145,30 @@ define_sys_register!( PARange[03 - 00] ); +define_sys_register!( + ID_AA64MMFR1_EL1, // ref. D19.2.65: AArch64 Memory Model Feature Register 1 + CMOW[59 - 56], + TIDCP1[55 - 52], + nTLBPA[51 - 48], + AFP[47 - 44], + HCX[43 - 40], + ETS[39 - 36], + TWED[35 - 32], + XNX[31 - 28], + SpecSEI[27 - 24], + PAN[23 - 20], + LO[19 - 16], + HPDS[15 - 12], + VH[11 - 8], + VMID[7 - 4], + HAFDBS[3 - 0] +); + +pub mod mmfr1_vmid { + pub const VMIDBITS_8: u64 = 0; + pub const VMIDBITS_16: u64 = 2; +} + define_sys_register!( MAIR_EL2, // ref. D7.2.71: Memory Attribute Indirection Register Attr7[63 - 56], @@ -193,7 +217,26 @@ define_sys_register!( ); define_sys_register!( - VTCR_EL2, + VTCR_EL2, // ref. Virtualzation Translation Control Register + DS[32 - 32], + RES1[31 - 31], + // Non-secure stage 2 translation output address space for the Secure EL1&0 + // translation regime + // 0b0: All stage 2 translations for the Non-secure IPA space of the Secure EL1&0 + // translation regime acccess the Secure PA space + // 0b1: All stage 2 translations for the Non-secure IPA space of the secure EL1&0 + // trnaslation regmime access the non-secure PA space + NSA[30 - 30], + // Non-secure stage 2 translation table address space for the Secure EL1&0 + // translation regime + NSW[29 - 29], + HWU62[28 - 28], + HWU61[27 - 27], + HWU60[26 - 26], + HWU59[25 - 25], + RES0[24 - 23], + HD[22 - 22], + HA[21 - 21], VS[19 - 19], // VMID size. 0b0: 8bits, 0b1: 16bit PS[18 - 16], // Physical address size for the second stage of translation TG0[15 - 14], // Granule size (VTTBR_EL2) @@ -204,6 +247,13 @@ define_sys_register!( T0SZ[5 - 0] // Size offset of the memory region (TTBR0_EL2) ); +pub mod vtcr_sl0 { + pub const SL0_4K_L2: u64 = 0x0; + pub const SL0_4K_L1: u64 = 0x1; + pub const SL0_4K_L0: u64 = 0x2; + pub const SL0_4K_L3: u64 = 0x3; +} + pub mod tcr_paddr_size { // PS pub const PS_4G: u64 = 0b000; // 32bits @@ -256,6 +306,12 @@ define_sys_register!( FIPA[43 - 4] // ); +define_bits!( + HpfarEl2, // Ref. D13.2.55 + NS[63 - 63], + FIPA[43 - 4] // +); + define_sys_register!( FAR_EL2, // Ref. D13.2.55 OFFSET[11 - 0] @@ -396,7 +452,6 @@ define_iss_id!(ISS_ID_AA64ISAR1_EL1, 3, 0, 0, 6, 1); define_iss_id!(ISS_ID_AA64MMFR0_EL1, 3, 0, 0, 7, 0); -define_sys_register!(ID_AA64MMFR1_EL1); define_iss_id!(ISS_ID_AA64MMFR1_EL1, 3, 0, 0, 7, 1); define_sys_register!(ID_AA64MMFR2_EL1); diff --git a/rmm/src/lib.rs b/rmm/src/lib.rs index ac8119afd8f03..956dbeb2ce791 100644 --- a/rmm/src/lib.rs +++ b/rmm/src/lib.rs @@ -52,7 +52,6 @@ use armv9a::{bits_in_reg, regs::*}; pub unsafe fn start() { setup_mmu_cfg(); setup_el2(); - activate_stage2_mmu(); setup_gst(); Monitor::new().run(); @@ -84,25 +83,6 @@ unsafe fn setup_el2() { ICC_SRE_EL2.set(ICC_SRE_EL2::ENABLE | ICC_SRE_EL2::DIB | ICC_SRE_EL2::DFB | ICC_SRE_EL2::SRE); } -unsafe fn activate_stage2_mmu() { - // stage 2 intitial table: L1 with 1024 entries (2 continuous 4KB pages) - let vtcr_el2: u64 = bits_in_reg(VTCR_EL2::PS, tcr_paddr_size::PS_1T) - | bits_in_reg(VTCR_EL2::TG0, tcr_granule::G_4K) - | bits_in_reg(VTCR_EL2::SH0, tcr_shareable::INNER) - | bits_in_reg(VTCR_EL2::ORGN0, tcr_cacheable::WBWA) - | bits_in_reg(VTCR_EL2::IRGN0, tcr_cacheable::WBWA) - | bits_in_reg(VTCR_EL2::SL0, tcr_start_level::L1) - | bits_in_reg(VTCR_EL2::T0SZ, 24); // T0SZ, input address is 2^40 bytes - - // Invalidate the local I-cache so that any instructions fetched - // speculatively are discarded. - core::arch::asm!("ic iallu", "dsb nsh", "isb",); - - VTCR_EL2.set(vtcr_el2); - - core::arch::asm!("tlbi alle2", "dsb ish", "isb",); -} - unsafe fn setup_mmu_cfg() { core::arch::asm!("tlbi alle2is", "dsb ish", "isb",); diff --git a/rmm/src/rmi/rec/handlers.rs b/rmm/src/rmi/rec/handlers.rs index 4e563b5580571..7deb74457ed5c 100644 --- a/rmm/src/rmi/rec/handlers.rs +++ b/rmm/src/rmi/rec/handlers.rs @@ -1,6 +1,7 @@ use super::mpidr::MPIDR; use super::params::Params; use super::run::{Run, REC_ENTRY_FLAG_TRAP_WFE, REC_ENTRY_FLAG_TRAP_WFI}; +use super::vtcr::{activate_stage2_mmu, prepare_vtcr}; use super::Rec; use crate::event::{realmexit, Context, Mainloop, RsiHandle}; use crate::granule::{set_granule, set_granule_with_parent, GranuleState}; @@ -64,6 +65,7 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { { return Err(Error::RmiErrorInput); } + rec.set_vtcr(prepare_vtcr(rd)?); rd.inc_rec_index(); set_granule_with_parent(rd_granule.clone(), &mut rec_granule, GranuleState::Rec) @@ -126,6 +128,8 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { warn!("TWI(E) in HCR_EL2 is currently fixed to 'no trap'"); } + activate_stage2_mmu(rec); + let mut ret_ns; loop { ret_ns = true; diff --git a/rmm/src/rmi/rec/mod.rs b/rmm/src/rmi/rec/mod.rs index 6a1b6119e9ade..59cdc3d729485 100644 --- a/rmm/src/rmi/rec/mod.rs +++ b/rmm/src/rmi/rec/mod.rs @@ -2,6 +2,7 @@ pub mod handlers; pub mod mpidr; mod params; pub mod run; +pub mod vtcr; pub use self::handlers::set_event_handler; @@ -17,6 +18,7 @@ pub struct Rec { owner: usize, vcpuid: usize, ripas: Ripas, + vtcr: u64, } struct Ripas { @@ -49,6 +51,10 @@ impl Rec { self.ripas.state = state; } + pub fn set_vtcr(&mut self, vtcr: u64) { + self.vtcr = vtcr; + } + pub fn inc_ripas_addr(&mut self, size: u64) { self.ripas.addr += size; } @@ -64,6 +70,10 @@ impl Rec { pub fn ripas_end(&self) -> u64 { self.ripas.end } + + pub fn vtcr(&self) -> u64 { + self.vtcr + } } impl Content for Rec { diff --git a/rmm/src/rmi/rec/vtcr.rs b/rmm/src/rmi/rec/vtcr.rs new file mode 100644 index 0000000000000..578b21663de5d --- /dev/null +++ b/rmm/src/rmi/rec/vtcr.rs @@ -0,0 +1,55 @@ +use super::Rec; +use crate::rmi::error::Error; +use crate::rmi::realm::Rd; +use armv9a::bits_in_reg; +use armv9a::regs::*; + +fn is_feat_vmid16_present() -> bool { + unsafe { ID_AA64MMFR1_EL1.get_masked_value(ID_AA64MMFR1_EL1::VMID) == mmfr1_vmid::VMIDBITS_16 } +} + +pub fn prepare_vtcr(rd: &Rd) -> Result { + let s2_starting_level = rd.s2_starting_level(); + let ipa_bits = rd.ipa_bits(); + + // sl0 consists of 2 bits (2^2 == 4) + if !(s2_starting_level >= 0 && s2_starting_level <= 3) { + return Err(Error::RmiErrorInput); + } + + // t0sz consists of 6 bits (2^6 == 64) + if !(ipa_bits > 0 && ipa_bits <= 64) { + return Err(Error::RmiErrorInput); + } + + let mut vtcr_val = bits_in_reg(VTCR_EL2::PS, tcr_paddr_size::PS_1T) + | bits_in_reg(VTCR_EL2::TG0, tcr_granule::G_4K) + | bits_in_reg(VTCR_EL2::SH0, tcr_shareable::INNER) + | bits_in_reg(VTCR_EL2::ORGN0, tcr_cacheable::WBWA) + | bits_in_reg(VTCR_EL2::IRGN0, tcr_cacheable::WBWA) + | bits_in_reg(VTCR_EL2::NSA, 1) + | bits_in_reg(VTCR_EL2::RES1, 1); //XXX: not sure why RES1 is in default set in tf-rmm + + if is_feat_vmid16_present() { + vtcr_val |= bits_in_reg(VTCR_EL2::VS, 1); + } + + let sl0_array: [u64; 4] = [ + vtcr_sl0::SL0_4K_L0, + vtcr_sl0::SL0_4K_L1, + vtcr_sl0::SL0_4K_L2, + vtcr_sl0::SL0_4K_L3, + ]; + let sl0_val = sl0_array[s2_starting_level as usize]; + let t0sz_val = (64 - ipa_bits) as u64; + + vtcr_val |= bits_in_reg(VTCR_EL2::SL0, sl0_val) | bits_in_reg(VTCR_EL2::T0SZ, t0sz_val); + + Ok(vtcr_val) +} + +pub fn activate_stage2_mmu(rec: &Rec) { + unsafe { + VTCR_EL2.set(rec.vtcr()); + } +} From 9aea05d2dd55e20ff3fdc1b577cb4c92e898cdb2 Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Tue, 10 Oct 2023 17:21:54 +0900 Subject: [PATCH 10/12] Add rtt validity checks --- rmm/src/rmi/realm/mod.rs | 5 +++++ rmm/src/rmi/rtt.rs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/rmm/src/rmi/realm/mod.rs b/rmm/src/rmi/realm/mod.rs index 202ecbeac5232..e4dd397dc9845 100644 --- a/rmm/src/rmi/realm/mod.rs +++ b/rmm/src/rmi/realm/mod.rs @@ -7,6 +7,7 @@ use self::params::Params; use self::rd::State; use super::error::Error; use crate::event::Mainloop; +use crate::granule::GRANULE_SIZE; use crate::granule::{set_granule, GranuleState}; use crate::host::pointer::Pointer as HostPointer; use crate::listen; @@ -47,6 +48,10 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { if params.rtt_base as usize == rd { return Err(Error::RmiErrorInput); } + if !(params.rtt_base as usize % GRANULE_SIZE == 0) { + return Err(Error::RmiErrorInput); + } + let _ = get_granule_if!(params.rtt_base as usize, GranuleState::Delegated)?; // revisit rmi.create_realm() (is it necessary?) rmm.rmi diff --git a/rmm/src/rmi/rtt.rs b/rmm/src/rmi/rtt.rs index 5088a77813ca4..77a4f758374f1 100644 --- a/rmm/src/rmi/rtt.rs +++ b/rmm/src/rmi/rtt.rs @@ -7,10 +7,12 @@ use crate::granule::{is_not_in_realm, set_granule, GranuleState, GRANULE_SIZE}; use crate::host::pointer::Pointer as HostPointer; use crate::host::DataPage; use crate::listen; +use crate::realm::mm::stage2_tte::S2TTE; use crate::rmi; use crate::rmi::error::Error; use crate::{get_granule, get_granule_if, set_state_and_get_granule}; +pub const RTT_MIN_BLOCK_LEVEL: usize = 2; pub const RTT_PAGE_LEVEL: usize = 3; const RIPAS_EMPTY: u64 = 0; @@ -27,6 +29,23 @@ fn level_to_size(level: usize) -> u64 { } } +fn is_valid_rtt_cmd(ipa: usize, level: usize) -> bool { + if level > RTT_PAGE_LEVEL { + return false; + } + let mask = match level { + 0 => S2TTE::ADDR_L0_PAGE, + 1 => S2TTE::ADDR_L1_PAGE, + 2 => S2TTE::ADDR_L2_PAGE, + 3 => S2TTE::ADDR_L3_PAGE, + _ => unreachable!(), + }; + if ipa & mask as usize != ipa { + return false; + } + return true; +} + pub fn set_event_handler(mainloop: &mut Mainloop) { listen!(mainloop, rmi::RTT_CREATE, |arg, _ret, rmm| { let rmi = rmm.rmi; @@ -36,6 +55,9 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { let ipa = arg[2]; let level = arg[3]; + if !is_valid_rtt_cmd(ipa, level) { + return Err(Error::RmiErrorInput); + } rmi.rtt_create(rd.id(), rtt_addr, ipa, level)?; Ok(()) }); @@ -48,6 +70,9 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { let ipa = arg[2]; let level = arg[3]; + if !is_valid_rtt_cmd(ipa, level) { + return Err(Error::RmiErrorInput); + } rmi.rtt_destroy(rd, rtt_addr, ipa, level)?; Ok(()) }); @@ -60,6 +85,9 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { let level = arg[2]; if rd.state() != State::New { + return Err(Error::RmiErrorRealm); + } + if !is_valid_rtt_cmd(ipa, level) { return Err(Error::RmiErrorInput); } rmi.rtt_init_ripas(rd.id(), ipa, level)?; @@ -117,6 +145,9 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { let rd = rd_granule.content::(); let ipa = arg[1]; let level = arg[2]; + if !is_valid_rtt_cmd(ipa, level) { + return Err(Error::RmiErrorInput); + } let res = rmi.rtt_read_entry(rd.id(), ipa, level)?; ret[1..5].copy_from_slice(&res[0..4]); @@ -222,6 +253,9 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { let level = arg[2]; let host_s2tte = arg[3]; + if !is_valid_rtt_cmd(ipa, level) { + return Err(Error::RmiErrorInput); + } rmi.rtt_map_unprotected(rd, ipa, level, host_s2tte)?; Ok(()) }); @@ -236,6 +270,9 @@ pub fn set_event_handler(mainloop: &mut Mainloop) { let realm_id = rd.id(); let level = arg[2]; + if !is_valid_rtt_cmd(ipa, level) { + return Err(Error::RmiErrorInput); + } rmi.rtt_unmap_unprotected(realm_id, ipa, level)?; Ok(()) }); From 549cc2920ddd08915cbc205b3da92d9b596c3cd2 Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Tue, 10 Oct 2023 17:22:21 +0900 Subject: [PATCH 11/12] Fix check 9 in acs's rtt_create --- rmm/src/realm/registry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rmm/src/realm/registry.rs b/rmm/src/realm/registry.rs index 33329bfef7373..9e22fcd489059 100644 --- a/rmm/src/realm/registry.rs +++ b/rmm/src/realm/registry.rs @@ -411,7 +411,7 @@ impl crate::rmi::Interface for RMI { .page_table .lock() .ipa_to_pte(GuestPhysAddr::from(ipa), level - 1) - .ok_or(Error::RmiErrorRtt(0))?; //XXX: check this again + .ok_or(Error::RmiErrorInput)?; if last_level != level - 1 { return Err(Error::RmiErrorRtt(last_level)); From 49ce5f6b820bfd4862348bb8e64aeaa1d8e7d67f Mon Sep 17 00:00:00 2001 From: Changho Choi Date: Tue, 10 Oct 2023 17:23:16 +0900 Subject: [PATCH 12/12] Enable tf-a tests --- scripts/tests/tf-a-tests.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/tests/tf-a-tests.sh b/scripts/tests/tf-a-tests.sh index 0c8f2102cc5f9..1d9ecbe1cd291 100755 --- a/scripts/tests/tf-a-tests.sh +++ b/scripts/tests/tf-a-tests.sh @@ -48,17 +48,17 @@ sleep 20 check_result # islet-rmm tests -#$ROOT/scripts/fvp-cca -bo -nw=tf-a-tests -rmm=islet -#$ROOT/scripts/fvp-cca -ro -nw=tf-a-tests -rmm=islet & +$ROOT/scripts/fvp-cca -bo -nw=tf-a-tests -rmm=islet +$ROOT/scripts/fvp-cca -ro -nw=tf-a-tests -rmm=islet & -#sleep 20 +sleep 20 -#check_result +check_result # islet-rmm tests with rsi-test realm -#$ROOT/scripts/fvp-cca -bo -nw=tf-a-tests -rmm=islet -rm=rsi-test -#$ROOT/scripts/fvp-cca -ro -nw=tf-a-tests -rmm=islet -rm=rsi-test & +$ROOT/scripts/fvp-cca -bo -nw=tf-a-tests -rmm=islet -rm=rsi-test +$ROOT/scripts/fvp-cca -ro -nw=tf-a-tests -rmm=islet -rm=rsi-test & -#sleep 20 +sleep 20 -#check_result +check_result