1*ae047468SBoris Brezillon // SPDX-License-Identifier: GPL-2.0 or MIT 2*ae047468SBoris Brezillon 3*ae047468SBoris Brezillon //! Address space module. 4*ae047468SBoris Brezillon //! 5*ae047468SBoris Brezillon //! This module handles the hardware interaction for MMU operations through 6*ae047468SBoris Brezillon //! MMIO register access. 7*ae047468SBoris Brezillon //! 8*ae047468SBoris Brezillon 9*ae047468SBoris Brezillon use core::ops::Range; 10*ae047468SBoris Brezillon 11*ae047468SBoris Brezillon use kernel::{ 12*ae047468SBoris Brezillon device::{ 13*ae047468SBoris Brezillon Bound, 14*ae047468SBoris Brezillon Device, // 15*ae047468SBoris Brezillon }, // 16*ae047468SBoris Brezillon error::Result, 17*ae047468SBoris Brezillon io::{ 18*ae047468SBoris Brezillon poll, 19*ae047468SBoris Brezillon register::Array, 20*ae047468SBoris Brezillon Io, // 21*ae047468SBoris Brezillon }, 22*ae047468SBoris Brezillon iommu::pgtable::{ 23*ae047468SBoris Brezillon Config, 24*ae047468SBoris Brezillon IoPageTable, 25*ae047468SBoris Brezillon ARM64LPAES1, // 26*ae047468SBoris Brezillon }, 27*ae047468SBoris Brezillon num::Bounded, 28*ae047468SBoris Brezillon prelude::*, 29*ae047468SBoris Brezillon sizes::{ 30*ae047468SBoris Brezillon SZ_2M, 31*ae047468SBoris Brezillon SZ_4K, // 32*ae047468SBoris Brezillon }, 33*ae047468SBoris Brezillon sync::{ 34*ae047468SBoris Brezillon Arc, 35*ae047468SBoris Brezillon ArcBorrow, 36*ae047468SBoris Brezillon LockedBy, // 37*ae047468SBoris Brezillon }, 38*ae047468SBoris Brezillon time::Delta, // 39*ae047468SBoris Brezillon }; 40*ae047468SBoris Brezillon 41*ae047468SBoris Brezillon use crate::{ 42*ae047468SBoris Brezillon driver::IoMem, 43*ae047468SBoris Brezillon mmu::{ 44*ae047468SBoris Brezillon AsSlotManager, 45*ae047468SBoris Brezillon Mmu, // 46*ae047468SBoris Brezillon }, 47*ae047468SBoris Brezillon regs::{ 48*ae047468SBoris Brezillon mmu_control::mmu_as_control, 49*ae047468SBoris Brezillon mmu_control::mmu_as_control::*, 50*ae047468SBoris Brezillon MAX_AS, // 51*ae047468SBoris Brezillon }, 52*ae047468SBoris Brezillon slot::{ 53*ae047468SBoris Brezillon LockedSeat, 54*ae047468SBoris Brezillon Seat, 55*ae047468SBoris Brezillon SlotOperations, // 56*ae047468SBoris Brezillon }, // 57*ae047468SBoris Brezillon }; 58*ae047468SBoris Brezillon 59*ae047468SBoris Brezillon /// Address space configuration values to be written to MMU registers. 60*ae047468SBoris Brezillon #[derive(Clone, Copy)] 61*ae047468SBoris Brezillon struct AddressSpaceConfig { 62*ae047468SBoris Brezillon /// Translation configuration. Configures how the MMU walks the page table for this 63*ae047468SBoris Brezillon /// address space. 64*ae047468SBoris Brezillon transcfg: u64, 65*ae047468SBoris Brezillon 66*ae047468SBoris Brezillon /// Translation table base address. The address of the page table. 67*ae047468SBoris Brezillon transtab: u64, 68*ae047468SBoris Brezillon 69*ae047468SBoris Brezillon /// Memory attributes such as cacheability. 70*ae047468SBoris Brezillon memattr: u64, 71*ae047468SBoris Brezillon } 72*ae047468SBoris Brezillon 73*ae047468SBoris Brezillon /// Virtual memory (VM) address space data for use in MMU operations. 74*ae047468SBoris Brezillon #[pin_data] 75*ae047468SBoris Brezillon pub(crate) struct VmAsData<'drm> { 76*ae047468SBoris Brezillon /// This address-space seat tracks this VM's binding to a hardware address space slot. 77*ae047468SBoris Brezillon /// It can only be accessed when holding the `Mmu::as_manager` lock. 78*ae047468SBoris Brezillon as_seat: LockedSeat<AddressSpaceManager<'drm>, MAX_AS>, 79*ae047468SBoris Brezillon 80*ae047468SBoris Brezillon /// Virtual address bits for this address space. 81*ae047468SBoris Brezillon va_bits: u8, 82*ae047468SBoris Brezillon 83*ae047468SBoris Brezillon /// The page table which maps GPU virtual addresses to physical addresses for this VM. 84*ae047468SBoris Brezillon #[pin] 85*ae047468SBoris Brezillon pub(crate) page_table: IoPageTable<'drm, ARM64LPAES1>, 86*ae047468SBoris Brezillon } 87*ae047468SBoris Brezillon 88*ae047468SBoris Brezillon impl<'drm> VmAsData<'drm> { 89*ae047468SBoris Brezillon /// Creates VM address space data by initializing all of its fields. 90*ae047468SBoris Brezillon pub(crate) fn new<'a>( 91*ae047468SBoris Brezillon mmu: &'a Mmu<'drm>, 92*ae047468SBoris Brezillon dev: &'drm Device<Bound>, 93*ae047468SBoris Brezillon va_bits: u32, 94*ae047468SBoris Brezillon pa_bits: u32, 95*ae047468SBoris Brezillon ) -> impl pin_init::PinInit<VmAsData<'drm>, Error> + 'a { 96*ae047468SBoris Brezillon let pt_config = Config { 97*ae047468SBoris Brezillon quirks: 0, 98*ae047468SBoris Brezillon pgsize_bitmap: SZ_4K | SZ_2M, 99*ae047468SBoris Brezillon ias: va_bits, 100*ae047468SBoris Brezillon oas: pa_bits, 101*ae047468SBoris Brezillon coherent_walk: false, 102*ae047468SBoris Brezillon }; 103*ae047468SBoris Brezillon 104*ae047468SBoris Brezillon let page_table_init = IoPageTable::new(dev, pt_config); 105*ae047468SBoris Brezillon 106*ae047468SBoris Brezillon try_pin_init!(Self { 107*ae047468SBoris Brezillon as_seat: LockedBy::new(&mmu.as_manager, Seat::NoSeat), 108*ae047468SBoris Brezillon va_bits: va_bits as u8, 109*ae047468SBoris Brezillon page_table <- page_table_init, 110*ae047468SBoris Brezillon }? Error) 111*ae047468SBoris Brezillon } 112*ae047468SBoris Brezillon 113*ae047468SBoris Brezillon /// Computes the hardware configuration for this address space. 114*ae047468SBoris Brezillon fn as_config(&self) -> Result<AddressSpaceConfig> { 115*ae047468SBoris Brezillon let pt = &self.page_table; 116*ae047468SBoris Brezillon // The hardware computes the valid input address range as: 117*ae047468SBoris Brezillon // INA_BITS_VALID = min(HW_INA_BITS, 55 - INA_BITS) 118*ae047468SBoris Brezillon // To configure our desired va_bits, we solve for INA_BITS: 119*ae047468SBoris Brezillon // INA_BITS = 55 - va_bits 120*ae047468SBoris Brezillon // This assumes HW_INA_BITS (hardware capability) >= va_bits. 121*ae047468SBoris Brezillon let field = 55u64.checked_sub(self.va_bits.into()).ok_or(EINVAL)?; 122*ae047468SBoris Brezillon let ina_bits = 123*ae047468SBoris Brezillon match mmu_as_control::InaBits::try_from(Bounded::try_new(field).ok_or(EINVAL)?)? { 124*ae047468SBoris Brezillon mmu_as_control::InaBits::Reset => return Err(EINVAL), 125*ae047468SBoris Brezillon bits => bits, 126*ae047468SBoris Brezillon }; 127*ae047468SBoris Brezillon 128*ae047468SBoris Brezillon let transcfg = mmu_as_control::TRANSCFG::zeroed() 129*ae047468SBoris Brezillon .with_ptw_memattr(mmu_as_control::PtwMemattr::WriteBack) 130*ae047468SBoris Brezillon .with_r_allocate(true) 131*ae047468SBoris Brezillon .with_mode(mmu_as_control::AddressSpaceMode::Aarch64_4K) 132*ae047468SBoris Brezillon .with_ina_bits(ina_bits) 133*ae047468SBoris Brezillon .into_raw(); 134*ae047468SBoris Brezillon 135*ae047468SBoris Brezillon Ok(AddressSpaceConfig { 136*ae047468SBoris Brezillon transcfg, 137*ae047468SBoris Brezillon // SAFETY: The SlotManager holds an `Arc<VmAsData>` as SlotData while this 138*ae047468SBoris Brezillon // TTBR is programmed and stores that Arc in the active slot before 139*ae047468SBoris Brezillon // returning. Eviction flushes and disables the slot before releasing 140*ae047468SBoris Brezillon // the Arc; if eviction fails, the slot retains it. Therefore the page 141*ae047468SBoris Brezillon // table cannot be dropped while the GPU is using it. 142*ae047468SBoris Brezillon transtab: unsafe { pt.ttbr() }, 143*ae047468SBoris Brezillon memattr: MEMATTR::from_mair(pt.mair()).into_raw(), 144*ae047468SBoris Brezillon }) 145*ae047468SBoris Brezillon } 146*ae047468SBoris Brezillon } 147*ae047468SBoris Brezillon 148*ae047468SBoris Brezillon /// Coordinates all hardware-level address space operations through MMIO register 149*ae047468SBoris Brezillon /// operations including enabling, disabling, flushing, and updating address spaces. 150*ae047468SBoris Brezillon pub(crate) struct AddressSpaceManager<'drm> { 151*ae047468SBoris Brezillon /// Parent device used for logging. 152*ae047468SBoris Brezillon dev: &'drm Device<Bound>, 153*ae047468SBoris Brezillon 154*ae047468SBoris Brezillon /// Memory-mapped I/O region for GPU register access. 155*ae047468SBoris Brezillon iomem: Arc<IoMem<'drm>>, 156*ae047468SBoris Brezillon 157*ae047468SBoris Brezillon /// Bitmask of present address space slots from GPU_AS_PRESENT register. 158*ae047468SBoris Brezillon as_present: u32, 159*ae047468SBoris Brezillon } 160*ae047468SBoris Brezillon 161*ae047468SBoris Brezillon impl<'drm> AddressSpaceManager<'drm> { 162*ae047468SBoris Brezillon /// Creates a new address space manager. 163*ae047468SBoris Brezillon /// 164*ae047468SBoris Brezillon /// Initializes the manager with references to the platform device and 165*ae047468SBoris Brezillon /// I/O memory region, along with the bitmask of available AS slots. 166*ae047468SBoris Brezillon pub(super) fn new( 167*ae047468SBoris Brezillon dev: &'drm Device<Bound>, 168*ae047468SBoris Brezillon iomem: Arc<IoMem<'drm>>, 169*ae047468SBoris Brezillon as_present: u32, 170*ae047468SBoris Brezillon ) -> Result<AddressSpaceManager<'drm>> { 171*ae047468SBoris Brezillon if as_present.trailing_ones() != as_present.count_ones() { 172*ae047468SBoris Brezillon dev_err!( 173*ae047468SBoris Brezillon dev, 174*ae047468SBoris Brezillon "Sparse AS_PRESENT mask is unsupported: {:#x}", 175*ae047468SBoris Brezillon as_present 176*ae047468SBoris Brezillon ); 177*ae047468SBoris Brezillon return Err(EINVAL); 178*ae047468SBoris Brezillon } 179*ae047468SBoris Brezillon Ok(Self { 180*ae047468SBoris Brezillon dev, 181*ae047468SBoris Brezillon iomem, 182*ae047468SBoris Brezillon as_present, 183*ae047468SBoris Brezillon }) 184*ae047468SBoris Brezillon } 185*ae047468SBoris Brezillon 186*ae047468SBoris Brezillon /// Validates that an AS slot number is within range and present in hardware. 187*ae047468SBoris Brezillon /// 188*ae047468SBoris Brezillon /// Checks that the slot index is less than [`MAX_AS`] and that 189*ae047468SBoris Brezillon /// the corresponding bit is set in the `as_present` mask read from the GPU. 190*ae047468SBoris Brezillon /// 191*ae047468SBoris Brezillon /// Returns [`EINVAL`] if the slot is out of range or not present in hardware. 192*ae047468SBoris Brezillon fn validate_as_slot(&self, as_nr: usize) -> Result { 193*ae047468SBoris Brezillon if as_nr >= MAX_AS { 194*ae047468SBoris Brezillon dev_err!( 195*ae047468SBoris Brezillon self.dev, 196*ae047468SBoris Brezillon "AS slot {} out of valid range (max {})", 197*ae047468SBoris Brezillon as_nr, 198*ae047468SBoris Brezillon MAX_AS 199*ae047468SBoris Brezillon ); 200*ae047468SBoris Brezillon return Err(EINVAL); 201*ae047468SBoris Brezillon } 202*ae047468SBoris Brezillon 203*ae047468SBoris Brezillon if (self.as_present & (1 << as_nr)) == 0 { 204*ae047468SBoris Brezillon dev_err!( 205*ae047468SBoris Brezillon self.dev, 206*ae047468SBoris Brezillon "AS slot {} not present in hardware (AS_PRESENT={:#x})", 207*ae047468SBoris Brezillon as_nr, 208*ae047468SBoris Brezillon self.as_present 209*ae047468SBoris Brezillon ); 210*ae047468SBoris Brezillon return Err(EINVAL); 211*ae047468SBoris Brezillon } 212*ae047468SBoris Brezillon Ok(()) 213*ae047468SBoris Brezillon } 214*ae047468SBoris Brezillon 215*ae047468SBoris Brezillon /// Waits for an AS slot to become ready (not active). 216*ae047468SBoris Brezillon /// 217*ae047468SBoris Brezillon /// Returns an error if polling times out after 10ms or if register access fails. 218*ae047468SBoris Brezillon fn as_wait_ready(&self, as_nr: usize) -> Result { 219*ae047468SBoris Brezillon let io = &*self.iomem; 220*ae047468SBoris Brezillon let op = || { 221*ae047468SBoris Brezillon let status_reg = STATUS::try_at(as_nr).ok_or(EINVAL)?; 222*ae047468SBoris Brezillon Ok(io.read(status_reg)) 223*ae047468SBoris Brezillon }; 224*ae047468SBoris Brezillon let cond = |status: &STATUS| -> bool { !status.active_ext() }; 225*ae047468SBoris Brezillon poll::read_poll_timeout(op, cond, Delta::from_micros(50), Delta::from_millis(10))?; 226*ae047468SBoris Brezillon 227*ae047468SBoris Brezillon Ok(()) 228*ae047468SBoris Brezillon } 229*ae047468SBoris Brezillon 230*ae047468SBoris Brezillon /// Sends a command to an AS slot. 231*ae047468SBoris Brezillon /// 232*ae047468SBoris Brezillon /// Returns an error if waiting for ready times out or if register write fails. 233*ae047468SBoris Brezillon fn as_send_cmd(&mut self, as_nr: usize, cmd: MmuCommand) -> Result { 234*ae047468SBoris Brezillon self.as_wait_ready(as_nr)?; 235*ae047468SBoris Brezillon let io = &*self.iomem; 236*ae047468SBoris Brezillon let command_reg = COMMAND::try_at(as_nr).ok_or(EINVAL)?; 237*ae047468SBoris Brezillon io.write(command_reg, COMMAND::zeroed().with_command(cmd)); 238*ae047468SBoris Brezillon Ok(()) 239*ae047468SBoris Brezillon } 240*ae047468SBoris Brezillon 241*ae047468SBoris Brezillon /// Sends a command to an AS slot and waits for completion. 242*ae047468SBoris Brezillon /// 243*ae047468SBoris Brezillon /// Returns an error if sending the command fails or if waiting for completion times out. 244*ae047468SBoris Brezillon fn as_send_cmd_and_wait(&mut self, as_nr: usize, cmd: MmuCommand) -> Result { 245*ae047468SBoris Brezillon self.as_send_cmd(as_nr, cmd)?; 246*ae047468SBoris Brezillon self.as_wait_ready(as_nr)?; 247*ae047468SBoris Brezillon Ok(()) 248*ae047468SBoris Brezillon } 249*ae047468SBoris Brezillon 250*ae047468SBoris Brezillon /// Enables an AS slot with the provided configuration. 251*ae047468SBoris Brezillon /// 252*ae047468SBoris Brezillon /// Returns an error if the slot is invalid or if register writes/commands fail. 253*ae047468SBoris Brezillon fn as_enable(&mut self, as_nr: usize, as_config: &AddressSpaceConfig) -> Result { 254*ae047468SBoris Brezillon self.validate_as_slot(as_nr)?; 255*ae047468SBoris Brezillon 256*ae047468SBoris Brezillon let io = &*self.iomem; 257*ae047468SBoris Brezillon 258*ae047468SBoris Brezillon let transtab = as_config.transtab; 259*ae047468SBoris Brezillon io.write( 260*ae047468SBoris Brezillon TRANSTAB_LO::try_at(as_nr).ok_or(EINVAL)?, 261*ae047468SBoris Brezillon TRANSTAB_LO::from_raw(transtab as u32), 262*ae047468SBoris Brezillon ); 263*ae047468SBoris Brezillon io.write( 264*ae047468SBoris Brezillon TRANSTAB_HI::try_at(as_nr).ok_or(EINVAL)?, 265*ae047468SBoris Brezillon TRANSTAB_HI::from_raw((transtab >> 32) as u32), 266*ae047468SBoris Brezillon ); 267*ae047468SBoris Brezillon 268*ae047468SBoris Brezillon let transcfg = as_config.transcfg; 269*ae047468SBoris Brezillon io.write( 270*ae047468SBoris Brezillon TRANSCFG_LO::try_at(as_nr).ok_or(EINVAL)?, 271*ae047468SBoris Brezillon TRANSCFG_LO::from_raw(transcfg as u32), 272*ae047468SBoris Brezillon ); 273*ae047468SBoris Brezillon io.write( 274*ae047468SBoris Brezillon TRANSCFG_HI::try_at(as_nr).ok_or(EINVAL)?, 275*ae047468SBoris Brezillon TRANSCFG_HI::from_raw((transcfg >> 32) as u32), 276*ae047468SBoris Brezillon ); 277*ae047468SBoris Brezillon 278*ae047468SBoris Brezillon let memattr = as_config.memattr; 279*ae047468SBoris Brezillon io.write( 280*ae047468SBoris Brezillon MEMATTR_LO::try_at(as_nr).ok_or(EINVAL)?, 281*ae047468SBoris Brezillon MEMATTR_LO::from_raw(memattr as u32), 282*ae047468SBoris Brezillon ); 283*ae047468SBoris Brezillon io.write( 284*ae047468SBoris Brezillon MEMATTR_HI::try_at(as_nr).ok_or(EINVAL)?, 285*ae047468SBoris Brezillon MEMATTR_HI::from_raw((memattr >> 32) as u32), 286*ae047468SBoris Brezillon ); 287*ae047468SBoris Brezillon 288*ae047468SBoris Brezillon self.as_send_cmd_and_wait(as_nr, MmuCommand::Update)?; 289*ae047468SBoris Brezillon 290*ae047468SBoris Brezillon Ok(()) 291*ae047468SBoris Brezillon } 292*ae047468SBoris Brezillon 293*ae047468SBoris Brezillon /// Disables an AS slot and clears its configuration. 294*ae047468SBoris Brezillon /// 295*ae047468SBoris Brezillon /// Returns an error if the slot is invalid or if register writes/commands fail. 296*ae047468SBoris Brezillon fn as_disable(&mut self, as_nr: usize) -> Result { 297*ae047468SBoris Brezillon self.validate_as_slot(as_nr)?; 298*ae047468SBoris Brezillon 299*ae047468SBoris Brezillon // Flush AS before disabling 300*ae047468SBoris Brezillon self.as_send_cmd_and_wait(as_nr, MmuCommand::FlushMem)?; 301*ae047468SBoris Brezillon 302*ae047468SBoris Brezillon let io = &*self.iomem; 303*ae047468SBoris Brezillon 304*ae047468SBoris Brezillon io.write( 305*ae047468SBoris Brezillon TRANSTAB_LO::try_at(as_nr).ok_or(EINVAL)?, 306*ae047468SBoris Brezillon TRANSTAB_LO::from_raw(0), 307*ae047468SBoris Brezillon ); 308*ae047468SBoris Brezillon io.write( 309*ae047468SBoris Brezillon TRANSTAB_HI::try_at(as_nr).ok_or(EINVAL)?, 310*ae047468SBoris Brezillon TRANSTAB_HI::from_raw(0), 311*ae047468SBoris Brezillon ); 312*ae047468SBoris Brezillon 313*ae047468SBoris Brezillon io.write( 314*ae047468SBoris Brezillon MEMATTR_LO::try_at(as_nr).ok_or(EINVAL)?, 315*ae047468SBoris Brezillon MEMATTR_LO::from_raw(0), 316*ae047468SBoris Brezillon ); 317*ae047468SBoris Brezillon io.write( 318*ae047468SBoris Brezillon MEMATTR_HI::try_at(as_nr).ok_or(EINVAL)?, 319*ae047468SBoris Brezillon MEMATTR_HI::from_raw(0), 320*ae047468SBoris Brezillon ); 321*ae047468SBoris Brezillon 322*ae047468SBoris Brezillon let transcfg = TRANSCFG::zeroed() 323*ae047468SBoris Brezillon .with_mode(AddressSpaceMode::Unmapped) 324*ae047468SBoris Brezillon .into_raw(); 325*ae047468SBoris Brezillon 326*ae047468SBoris Brezillon io.write( 327*ae047468SBoris Brezillon TRANSCFG_LO::try_at(as_nr).ok_or(EINVAL)?, 328*ae047468SBoris Brezillon TRANSCFG_LO::from_raw(transcfg as u32), 329*ae047468SBoris Brezillon ); 330*ae047468SBoris Brezillon io.write( 331*ae047468SBoris Brezillon TRANSCFG_HI::try_at(as_nr).ok_or(EINVAL)?, 332*ae047468SBoris Brezillon TRANSCFG_HI::from_raw((transcfg >> 32) as u32), 333*ae047468SBoris Brezillon ); 334*ae047468SBoris Brezillon 335*ae047468SBoris Brezillon self.as_send_cmd_and_wait(as_nr, MmuCommand::Update)?; 336*ae047468SBoris Brezillon 337*ae047468SBoris Brezillon Ok(()) 338*ae047468SBoris Brezillon } 339*ae047468SBoris Brezillon 340*ae047468SBoris Brezillon /// Locks a region of the translation tables for an atomic update. 341*ae047468SBoris Brezillon /// 342*ae047468SBoris Brezillon /// Programs the MMU [`LOCKADDR`] register for the given address space and issues 343*ae047468SBoris Brezillon /// the lock command. The hardware rounds the requested range up to a 344*ae047468SBoris Brezillon /// power-of-two region aligned to its size. 345*ae047468SBoris Brezillon /// 346*ae047468SBoris Brezillon /// Returns an error if the slot is invalid or if register writes/commands fail. 347*ae047468SBoris Brezillon fn as_start_update(&mut self, as_nr: usize, region: &Range<u64>) -> Result { 348*ae047468SBoris Brezillon self.validate_as_slot(as_nr)?; 349*ae047468SBoris Brezillon 350*ae047468SBoris Brezillon // Avoid both an empty range and an inverted range. 351*ae047468SBoris Brezillon if region.start >= region.end { 352*ae047468SBoris Brezillon return Err(EINVAL); 353*ae047468SBoris Brezillon } 354*ae047468SBoris Brezillon 355*ae047468SBoris Brezillon // The lock operates on full 64-byte cache lines of translation table entries. 356*ae047468SBoris Brezillon // Since each translation table entry (TTE) is 8 bytes, a cache line has 8 TTEs. 357*ae047468SBoris Brezillon // Since each TTE maps one page, the minimum locked region size will be 8 pages. 358*ae047468SBoris Brezillon // 359*ae047468SBoris Brezillon // With 4KiB pages (Aarch64_4K mode), the minimum locked region is 32KiB. 360*ae047468SBoris Brezillon let lock_region_min_size: u64 = 4096 * 8; 361*ae047468SBoris Brezillon 362*ae047468SBoris Brezillon // Count the number of trailing zero bits (zeros at the right/least-significant 363*ae047468SBoris Brezillon // end of the binary representation). For a power-of-two value, this equals the 364*ae047468SBoris Brezillon // base-2 exponent (e.g., 32 KiB = 2^15 → 15). 365*ae047468SBoris Brezillon let lock_region_min_size_log2 = lock_region_min_size.trailing_zeros() as u8; 366*ae047468SBoris Brezillon 367*ae047468SBoris Brezillon // XOR the first and last addresses to identify which bits differ between them. 368*ae047468SBoris Brezillon // The highest set bit in the result determines the exponent of the smallest 369*ae047468SBoris Brezillon // power-of-two region that can contain both addresses. 370*ae047468SBoris Brezillon // 371*ae047468SBoris Brezillon // Example: 372*ae047468SBoris Brezillon // addr_xor = 0x1000 ^ 0x2FFF = 0x3FFF 373*ae047468SBoris Brezillon // highest set bit in 0x3FFF is bit 13 374*ae047468SBoris Brezillon // minimum region size = 2^(13 + 1) = 16 KiB 375*ae047468SBoris Brezillon let addr_xor = region.start ^ (region.end - 1); 376*ae047468SBoris Brezillon let region_size_log2 = 64 - addr_xor.leading_zeros() as u8; 377*ae047468SBoris Brezillon 378*ae047468SBoris Brezillon let lock_region_log2 = core::cmp::max(region_size_log2, lock_region_min_size_log2); 379*ae047468SBoris Brezillon 380*ae047468SBoris Brezillon let lock_region_size = 1u64.checked_shl(lock_region_log2.into()).ok_or(EINVAL)?; 381*ae047468SBoris Brezillon // Align the LOCKADDR base address down to the lock region size (1 << lock_region_log2). 382*ae047468SBoris Brezillon // 383*ae047468SBoris Brezillon // The MMU ignores the low lock_region_log2 bits of LOCKADDR base, so ensure 384*ae047468SBoris Brezillon // they are cleared in software to avoid ambiguity. 385*ae047468SBoris Brezillon // 386*ae047468SBoris Brezillon // Example: 387*ae047468SBoris Brezillon // lock_region_log2 = 14 (16 KiB) 388*ae047468SBoris Brezillon // region.start = 0x1000 389*ae047468SBoris Brezillon // lockaddr_base = 0x1000 & ~(0x3FFF) = 0x0000 390*ae047468SBoris Brezillon let lockaddr_base = region.start & !(lock_region_size - 1); 391*ae047468SBoris Brezillon 392*ae047468SBoris Brezillon // The LOCKADDR size field encodes the lock region size as log2(size) - 1, 393*ae047468SBoris Brezillon // per the hardware definition. For example, a 32 KiB region is encoded as 14 394*ae047468SBoris Brezillon // because log2(32 KiB) = 15. 395*ae047468SBoris Brezillon let lockaddr_size = lock_region_log2 - 1; 396*ae047468SBoris Brezillon 397*ae047468SBoris Brezillon let io = &*self.iomem; 398*ae047468SBoris Brezillon 399*ae047468SBoris Brezillon // The LOCKADDR base field stores address bits 63:12, so remove the low 12 bits 400*ae047468SBoris Brezillon // before passing this value to the register macro helper. 401*ae047468SBoris Brezillon // These bits are guaranteed to be zero anyway because of the minimum 402*ae047468SBoris Brezillon // size of the locked region. 403*ae047468SBoris Brezillon let lockaddr_base_field = lockaddr_base >> 12; 404*ae047468SBoris Brezillon let lockaddr_val = LOCKADDR::zeroed() 405*ae047468SBoris Brezillon .try_with_size(lockaddr_size)? 406*ae047468SBoris Brezillon .try_with_base(lockaddr_base_field)? 407*ae047468SBoris Brezillon .into_raw(); 408*ae047468SBoris Brezillon 409*ae047468SBoris Brezillon io.write( 410*ae047468SBoris Brezillon LOCKADDR_LO::try_at(as_nr).ok_or(EINVAL)?, 411*ae047468SBoris Brezillon LOCKADDR_LO::from_raw(lockaddr_val as u32), 412*ae047468SBoris Brezillon ); 413*ae047468SBoris Brezillon io.write( 414*ae047468SBoris Brezillon LOCKADDR_HI::try_at(as_nr).ok_or(EINVAL)?, 415*ae047468SBoris Brezillon LOCKADDR_HI::from_raw((lockaddr_val >> 32) as u32), 416*ae047468SBoris Brezillon ); 417*ae047468SBoris Brezillon 418*ae047468SBoris Brezillon self.as_send_cmd_and_wait(as_nr, MmuCommand::Lock) 419*ae047468SBoris Brezillon } 420*ae047468SBoris Brezillon 421*ae047468SBoris Brezillon /// Completes an atomic translation table update. 422*ae047468SBoris Brezillon /// 423*ae047468SBoris Brezillon /// Returns an error if the slot is invalid or if the flush command fails. 424*ae047468SBoris Brezillon fn as_end_update(&mut self, as_nr: usize) -> Result { 425*ae047468SBoris Brezillon self.validate_as_slot(as_nr)?; 426*ae047468SBoris Brezillon self.as_send_cmd_and_wait(as_nr, MmuCommand::FlushPt)?; 427*ae047468SBoris Brezillon Ok(()) 428*ae047468SBoris Brezillon } 429*ae047468SBoris Brezillon 430*ae047468SBoris Brezillon /// Flushes the translation table cache for an AS slot. 431*ae047468SBoris Brezillon /// 432*ae047468SBoris Brezillon /// Returns an error if the slot is invalid or if the flush command fails. 433*ae047468SBoris Brezillon fn as_flush(&mut self, as_nr: usize) -> Result { 434*ae047468SBoris Brezillon self.validate_as_slot(as_nr)?; 435*ae047468SBoris Brezillon self.as_send_cmd_and_wait(as_nr, MmuCommand::FlushPt) 436*ae047468SBoris Brezillon } 437*ae047468SBoris Brezillon } 438*ae047468SBoris Brezillon 439*ae047468SBoris Brezillon impl<'drm> SlotOperations<MAX_AS> for AddressSpaceManager<'drm> { 440*ae047468SBoris Brezillon /// VM address space data associated with a hardware slot. 441*ae047468SBoris Brezillon type SlotData = Arc<VmAsData<'drm>>; 442*ae047468SBoris Brezillon 443*ae047468SBoris Brezillon fn seat(slot_data: &Self::SlotData) -> &LockedSeat<Self, MAX_AS> { 444*ae047468SBoris Brezillon &slot_data.as_seat 445*ae047468SBoris Brezillon } 446*ae047468SBoris Brezillon 447*ae047468SBoris Brezillon /// Activates a VM in a hardware slot. 448*ae047468SBoris Brezillon fn activate(&mut self, slot_idx: usize, slot_data: &Self::SlotData) -> Result { 449*ae047468SBoris Brezillon let as_config = slot_data.as_config()?; 450*ae047468SBoris Brezillon self.as_enable(slot_idx, &as_config) 451*ae047468SBoris Brezillon } 452*ae047468SBoris Brezillon 453*ae047468SBoris Brezillon /// Evicts a VM from a hardware slot. 454*ae047468SBoris Brezillon fn evict(&mut self, slot_idx: usize, _slot_data: &Self::SlotData) -> Result { 455*ae047468SBoris Brezillon self.as_flush(slot_idx)?; 456*ae047468SBoris Brezillon self.as_disable(slot_idx)?; 457*ae047468SBoris Brezillon Ok(()) 458*ae047468SBoris Brezillon } 459*ae047468SBoris Brezillon } 460*ae047468SBoris Brezillon 461*ae047468SBoris Brezillon impl<'drm> AsSlotManager<'drm> { 462*ae047468SBoris Brezillon /// Locks a region for translation table updates if the VM has an active slot. 463*ae047468SBoris Brezillon pub(super) fn start_vm_update( 464*ae047468SBoris Brezillon &mut self, 465*ae047468SBoris Brezillon vm_as_data: &VmAsData<'drm>, 466*ae047468SBoris Brezillon region: &Range<u64>, 467*ae047468SBoris Brezillon ) -> Result { 468*ae047468SBoris Brezillon let seat = vm_as_data.as_seat.access(self); 469*ae047468SBoris Brezillon match seat.slot() { 470*ae047468SBoris Brezillon Some(slot) => { 471*ae047468SBoris Brezillon let as_nr = slot as usize; 472*ae047468SBoris Brezillon self.as_start_update(as_nr, region) 473*ae047468SBoris Brezillon } 474*ae047468SBoris Brezillon _ => Ok(()), 475*ae047468SBoris Brezillon } 476*ae047468SBoris Brezillon } 477*ae047468SBoris Brezillon 478*ae047468SBoris Brezillon /// Completes translation table updates and unlocks the region. 479*ae047468SBoris Brezillon pub(super) fn end_vm_update(&mut self, vm_as_data: &VmAsData<'drm>) -> Result { 480*ae047468SBoris Brezillon let seat = vm_as_data.as_seat.access(self); 481*ae047468SBoris Brezillon match seat.slot() { 482*ae047468SBoris Brezillon Some(slot) => { 483*ae047468SBoris Brezillon let as_nr = slot as usize; 484*ae047468SBoris Brezillon self.as_end_update(as_nr) 485*ae047468SBoris Brezillon } 486*ae047468SBoris Brezillon _ => Ok(()), 487*ae047468SBoris Brezillon } 488*ae047468SBoris Brezillon } 489*ae047468SBoris Brezillon 490*ae047468SBoris Brezillon /// Flushes the translation table cache if the VM has an active slot. 491*ae047468SBoris Brezillon pub(super) fn flush_vm(&mut self, vm_as_data: &VmAsData<'drm>) -> Result { 492*ae047468SBoris Brezillon let seat = vm_as_data.as_seat.access(self); 493*ae047468SBoris Brezillon match seat.slot() { 494*ae047468SBoris Brezillon Some(slot) => { 495*ae047468SBoris Brezillon let as_nr = slot as usize; 496*ae047468SBoris Brezillon self.as_flush(as_nr) 497*ae047468SBoris Brezillon } 498*ae047468SBoris Brezillon _ => Ok(()), 499*ae047468SBoris Brezillon } 500*ae047468SBoris Brezillon } 501*ae047468SBoris Brezillon 502*ae047468SBoris Brezillon /// Activates a VM by assigning it to a hardware slot. 503*ae047468SBoris Brezillon pub(super) fn activate_vm(&mut self, vm_as_data: ArcBorrow<'_, VmAsData<'drm>>) -> Result { 504*ae047468SBoris Brezillon self.activate(vm_as_data.into()) 505*ae047468SBoris Brezillon } 506*ae047468SBoris Brezillon 507*ae047468SBoris Brezillon /// Deactivates a VM by evicting it from its hardware slot. 508*ae047468SBoris Brezillon pub(super) fn deactivate_vm(&mut self, vm_as_data: &VmAsData<'drm>) -> Result { 509*ae047468SBoris Brezillon self.evict(&vm_as_data.as_seat) 510*ae047468SBoris Brezillon } 511*ae047468SBoris Brezillon } 512