diff --git a/platform/aarch64/qemu-gicv3/image/dts/zone1-linux.dts b/platform/aarch64/qemu-gicv3/image/dts/zone1-linux.dts index e2d4a8af..9239584b 100644 --- a/platform/aarch64/qemu-gicv3/image/dts/zone1-linux.dts +++ b/platform/aarch64/qemu-gicv3/image/dts/zone1-linux.dts @@ -37,12 +37,43 @@ reg = <0x0 0x50000000 0x0 0x30000000>; }; - gic@8000000 { + intc@8000000 { + phandle = <0x01>; + interrupts = <0x01 0x09 0x04>; + reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; + #redistributor-regions = <0x01>; compatible = "arm,gic-v3"; - #interrupt-cells = <0x03>; + ranges; + #size-cells = <0x02>; + #address-cells = <0x02>; interrupt-controller; - reg = <0x00 0x8000000 0x00 0x10000 0x00 0x80a0000 0x00 0xf60000>; - phandle = <0x01>; + #interrupt-cells = <0x03>; + + its@8080000 { + phandle = <0x8006>; + reg = <0x00 0x8080000 0x00 0x20000>; + #msi-cells = <0x01>; + msi-controller; + compatible = "arm,gic-v3-its"; + }; + }; + + pcie@10000000 { + interrupt-map-mask = <0x1800 0x00 0x00 0x07>; + interrupt-map = <0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x01 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x01 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x01 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x05 0x04>; + #interrupt-cells = <0x01>; + ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 + 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 + 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>; + reg = <0x40 0x10000000 0x00 0x10000000>; + msi-map = <0x00 0x8006 0x00 0x10000>; + dma-coherent; + bus-range = <0x00 0xff>; + linux,pci-domain = <0x00>; + #size-cells = <0x02>; + #address-cells = <0x03>; + device_type = "pci"; + compatible = "pci-host-ecam-generic"; }; apb-pclk { diff --git a/src/arch/aarch64/iommu.rs b/src/arch/aarch64/iommu.rs index 6773d2f7..1b7110d5 100644 --- a/src/arch/aarch64/iommu.rs +++ b/src/arch/aarch64/iommu.rs @@ -348,7 +348,6 @@ impl CmdQueue { pub struct Smmuv3 { rp: &'static RegisterPage, strtab: LinearStreamTable, - iommu_pt_list: Vec>, cmdq: CmdQueue, } @@ -358,20 +357,13 @@ impl Smmuv3 { let mut r = Self { rp: rp, strtab: LinearStreamTable::new(), - iommu_pt_list: vec![], cmdq: CmdQueue::new(), }; - for _ in 0..MAX_ZONE_NUM { - r.iommu_pt_list.push(new_s2_memory_set()); - } - - info!("pagetables for iommu, init done!"); - r.check_env(); - r.init_limited_pt(); r.init_structures(); r.device_reset(); + r } @@ -413,47 +405,6 @@ impl Smmuv3 { } } - fn init_limited_pt(&mut self) { - // its - for pt in self.iommu_pt_list.iter_mut() { - pt.insert(MemoryRegion::new_with_offset_mapper( - 0x8080000 as GuestPhysAddr, - 0x8080000, - 0x20000, - MemFlags::READ | MemFlags::WRITE, - )) - .ok(); - } - - // ram - self.iommu_pt_list[0] - .insert(MemoryRegion::new_with_offset_mapper( - 0x80000000 as GuestPhysAddr, - 0x80000000, - 0x50000000, - MemFlags::READ | MemFlags::WRITE, - )) - .ok(); - - self.iommu_pt_list[1] - .insert(MemoryRegion::new_with_offset_mapper( - 0x50000000 as GuestPhysAddr, - 0x50000000, - 0x30000000, - MemFlags::READ | MemFlags::WRITE, - )) - .ok(); - - self.iommu_pt_list[2] - .insert(MemoryRegion::new_with_offset_mapper( - 0x80000000 as GuestPhysAddr, - 0x80000000, - 0x10000000, - MemFlags::READ | MemFlags::WRITE, - )) - .ok(); - } - fn init_structures(&mut self) { self.init_strtab(); self.init_queues(); @@ -545,13 +496,12 @@ impl Smmuv3 { } // s1 bypass and s2 translate - fn write_ste(&mut self, sid: usize, vmid: usize) { + fn write_ste(&mut self, sid: usize, vmid: usize, root_pt: usize) { self.sync_ste(sid); assert!(vmid < MAX_ZONE_NUM, "Invalid zone id!"); - self.strtab - .write_ste(sid, vmid, self.iommu_pt_list[vmid].root_paddr()); + self.strtab.write_ste(sid, vmid, root_pt); } // invalidate the ste @@ -582,13 +532,8 @@ static SMMUV3: spin::Once> = spin::Once::new(); /// smmuv3 init pub fn iommu_init() { - #[cfg(feature = "iommu")] - { - info!("Smmuv3 init..."); - SMMUV3.call_once(|| Mutex::new(Smmuv3::new())); - } - #[cfg(not(feature = "iommu"))] - info!("Smmuv3 init: do nothing now"); + info!("Smmuv3 init..."); + SMMUV3.call_once(|| Mutex::new(Smmuv3::new())); } /// smmuv3_base @@ -604,15 +549,7 @@ pub fn smmuv3_size() -> usize { } /// write ste -pub fn iommu_add_device(vmid: usize, sid: usize) { - #[cfg(feature = "iommu")] - { - let mut smmu = SMMUV3.get().unwrap().lock(); - smmu.write_ste(sid as _, vmid as _); - } - #[cfg(not(feature = "iommu"))] - info!( - "aarch64: iommu_add_device: do nothing now, vmid: {}, sid: {}", - vmid, sid - ); +pub fn iommu_add_device(vmid: usize, sid: usize, root_pt: usize) { + let mut smmu = SMMUV3.get().unwrap().lock(); + smmu.write_ste(sid as _, vmid as _, root_pt as _); } diff --git a/src/arch/aarch64/zone.rs b/src/arch/aarch64/zone.rs index b7e8e90b..dd14fc0a 100644 --- a/src/arch/aarch64/zone.rs +++ b/src/arch/aarch64/zone.rs @@ -60,6 +60,58 @@ impl Zone { Ok(()) } + pub fn iommu_pt_init( + &mut self, + mem_regions: &[HvConfigMemoryRegion], + hv_config: &HvArchZoneConfig, + ) -> HvResult { + // Create a new stage 2 page table for iommu. + // Only map the memory regions that are possible to be accessed by devices as DMA buffer. + + let pt = self.iommu_pt.as_mut().unwrap(); + let flags = MemFlags::READ | MemFlags::WRITE; + for mem_region in mem_regions.iter() { + match mem_region.mem_type { + MEM_TYPE_RAM => { + pt.insert(MemoryRegion::new_with_offset_mapper( + mem_region.virtual_start as GuestPhysAddr, + mem_region.physical_start as HostPhysAddr, + mem_region.size as _, + flags, + ))?; + info!( + "iommu map: vaddr:{} - paddr:{}", + mem_region.virtual_start, mem_region.physical_start + ); + } + _ => { + // pass + } + } + } + + match hv_config.gic_config { + GicConfig::Gicv3(ref gicv3_config) => { + if gicv3_config.gits_size != 0 { + // map gits + pt.insert(MemoryRegion::new_with_offset_mapper( + gicv3_config.gits_base as GuestPhysAddr, + gicv3_config.gits_base as HostPhysAddr, + gicv3_config.gits_size as _, + flags | MemFlags::IO, + ))?; + info!( + "iommu map: vaddr:{} - paddr:{}", + gicv3_config.gits_base, gicv3_config.gits_base + ); + } + } + _ => {} + } + + Ok(()) + } + pub fn arch_zone_configuration(&mut self, config: &HvZoneConfig) -> HvResult { self.ivc_init(config.ivc_config()); Ok(()) diff --git a/src/device/irqchip/gicv3/gits.rs b/src/device/irqchip/gicv3/gits.rs index b43c2849..e3ad3b75 100644 --- a/src/device/irqchip/gicv3/gits.rs +++ b/src/device/irqchip/gicv3/gits.rs @@ -37,7 +37,7 @@ pub const GITS_UMSIR: usize = 0x0048; // unmapped msi pub const GITS_CBASER: usize = 0x0080; // the addr of command queue pub const GITS_CWRITER: usize = 0x0088; // rw, write an command to the cmdq, write this reg to tell hw pub const GITS_CREADR: usize = 0x0090; // read-only, hardware changes it -pub const GITS_BASER: usize = 0x0100; // itt, desc +pub const GITS_BASER: usize = 0x0100; // device table, itt, desc pub const GITS_COLLECTION_BASER: usize = GITS_BASER + 0x8; pub const GITS_TRANSLATER: usize = 0x10000 + 0x0040; // to signal an interrupt, written by devices @@ -73,19 +73,25 @@ fn vicid_to_icid(vicid: u64, cpu_bitmap: u64) -> Option { // created by root linux, and make a virtual one to non root pub struct DeviceTable { baser: usize, + mask: usize, + fix_val: usize, } impl DeviceTable { fn new() -> Self { let dt_baser_reg = host_gits_base() + GITS_BASER; let dt_baser = unsafe { ptr::read_volatile(dt_baser_reg as *mut u64) }; + let mask = 0x71f000000000000; + let fix_val = dt_baser & mask; Self { baser: dt_baser as _, + mask: mask as _, + fix_val: fix_val as _, } } fn set_baser(&mut self, value: usize) { - self.baser = value; + self.baser = (value & !self.mask) | self.fix_val; } fn read_baser(&self) -> usize { @@ -95,19 +101,25 @@ impl DeviceTable { pub struct CollectionTable { baser: usize, + mask: usize, + fix_val: usize, } impl CollectionTable { fn new() -> Self { let ct_baser_reg = host_gits_base() + GITS_COLLECTION_BASER; let ct_baser = unsafe { ptr::read_volatile(ct_baser_reg as *mut u64) }; + let mask = 0x71f000000000000; + let fix_val = ct_baser & mask; Self { baser: ct_baser as _, + mask: mask as _, + fix_val: fix_val as _, } } fn set_baser(&mut self, value: usize) { - self.baser = value; + self.baser = (value & !self.mask) | self.fix_val; } fn read_baser(&self) -> usize { @@ -121,8 +133,8 @@ pub struct Cmdq { writer: usize, frame: Frame, - phy_base_list: [usize; MAX_ZONE_NUM], - cbaser_list: [usize; MAX_ZONE_NUM], + phy_base_list: [usize; MAX_ZONE_NUM], // the real phy addr for vm cmdq + cbaser_list: [usize; MAX_ZONE_NUM], // the v register for vm creadr_list: [usize; MAX_ZONE_NUM], cwriter_list: [usize; MAX_ZONE_NUM], cmdq_page_num: [usize; MAX_ZONE_NUM], @@ -131,7 +143,6 @@ pub struct Cmdq { impl Cmdq { fn new() -> Self { let f = Frame::new_contiguous_with_base(CMDQ_PAGES_NUM, 16).unwrap(); - info!("ITS cmdq base: 0x{:x}", f.start_paddr()); let r = Self { phy_addr: f.start_paddr(), readr: 0, @@ -154,8 +165,8 @@ impl Cmdq { val = val | (CMDQ_PAGES_NUM - 1); // 16 contigous 4KB pages let ctrl = host_gits_base() + GITS_CTRL; unsafe { - let origin_ctrl = ptr::read_volatile(ctrl as *mut u64); - ptr::write_volatile(ctrl as *mut u64, origin_ctrl & 0xfffffffffffffffeu64); // turn off, vm will turn on this ctrl + let origin_ctrl = ptr::read_volatile(ctrl as *mut u32); + ptr::write_volatile(ctrl as *mut u32, origin_ctrl & 0xfffffffeu32); // turn off, vm will turn on this ctrl ptr::write_volatile(reg as *mut u64, val as u64); ptr::write_volatile(writer as *mut u64, 0 as u64); // init cwriter } @@ -164,9 +175,15 @@ impl Cmdq { fn set_cbaser(&mut self, zone_id: usize, value: usize) { assert!(zone_id < MAX_ZONE_NUM, "Invalid zone id!"); self.cbaser_list[zone_id] = value; - self.phy_base_list[zone_id] = value & 0xffffffffff000; + let gpa_base = value & 0xffffffffff000; + unsafe { + let phy_base = match this_zone().read().gpm.page_table_query(gpa_base) { + Ok(p) => self.phy_base_list[zone_id] = p.0, + _ => {} + }; + } self.cmdq_page_num[zone_id] = (value & 0xff) + 1; // get the page num - info!( + debug!( "zone_id: {}, cmdq base: {:#x}, page num: {}", zone_id, self.phy_base_list[zone_id], self.cmdq_page_num[zone_id] ); @@ -182,7 +199,7 @@ impl Cmdq { if value == self.creadr_list[zone_id] { // if the off vmm gonna read is equal to the cwriter, it means that // the first write cmd is not sent to the hw, so we ignore it. - trace!("ignore first write"); + debug!("ignore first write"); } else { self.insert_cmd(zone_id, value); } @@ -205,7 +222,7 @@ impl Cmdq { self.creadr_list[zone_id] = writer; } - // it's ok to add qemu-args: -trace gicv3_gits_cmd_*, remember to remain `enable one lpi` + // it's ok to add qemu-args: -info gicv3_gits_cmd_*, remember to remain `enable one lpi` // we need changge vicid to icid here fn analyze_cmd(&self, value: [u64; 4]) -> [u64; 4] { let code = (value[0] & 0xff) as usize; @@ -223,7 +240,7 @@ impl Cmdq { new_cmd[2] &= !0xffffu64; new_cmd[2] |= icid & 0xffff; enable_one_lpi((event - 8192) as _); - info!( + debug!( "MAPI cmd, for device {:#x}, event = intid = {:#x} -> vicid {:#x} (icid {:#x})", id >> 32, event, @@ -233,10 +250,25 @@ impl Cmdq { } 0x08 => { let id = value[0] & 0xffffffff00000000; - let itt_base = (value[2] & 0x000fffffffffffff) >> 8; - trace!( + let itt_base = value[2] & 0x000fffffffffff00; // the lowest 8 bits are zeros + debug!( + "MAPD cmd, for device {:#x}, itt base {:#x}", + id >> 32, + itt_base + ); + let phys_itt_base = unsafe { + this_zone() + .read() + .gpm + .page_table_query(itt_base as _) + .unwrap() + .0 + }; + new_cmd[2] &= !0x000fffffffffff00u64; + new_cmd[2] |= phys_itt_base as u64; + debug!( "MAPD cmd, set ITT: {:#x} to device {:#x}", - itt_base, + phys_itt_base, id >> 32 ); } @@ -250,7 +282,7 @@ impl Cmdq { new_cmd[2] &= !0xffffu64; new_cmd[2] |= icid & 0xffff; enable_one_lpi((intid - 8192) as _); - info!( + debug!( "MAPTI cmd, for device {:#x}, event {:#x} -> vicid {:#x} (icid {:#x}) + intid {:#x}", id >> 32, event, @@ -266,33 +298,34 @@ impl Cmdq { new_cmd[2] &= !0xffffu64; new_cmd[2] |= icid & 0xffff; let rd_base = (value[2] >> 16) & 0x7ffffffff; - info!( + debug!( "MAPC cmd, vicid {:#x} (icid {:#x}) -> redist {:#x}", vicid, icid, rd_base ); } 0x05 => { - trace!("SYNC cmd"); + debug!("SYNC cmd"); } 0x04 => { - trace!("CLEAR cmd"); + debug!("CLEAR cmd"); } 0x0f => { - trace!("DISCARD cmd"); + debug!("DISCARD cmd"); } 0x03 => { - trace!("INT cmd"); + debug!("INT cmd"); } 0x0c => { - trace!("INV cmd"); + debug!("INV cmd"); } 0x0d => { - trace!("INVALL cmd"); + debug!("INVALL cmd"); } _ => { - trace!("other cmd, code: 0x{:x}", code); + debug!("other cmd, code: 0x{:x}", code); } } + new_cmd } @@ -312,7 +345,7 @@ impl Cmdq { }; let cmd_num = cmd_size / PER_CMD_BYTES; - trace!("cmd size: {:#x}, cmd num: {:#x}", cmd_size, cmd_num); + debug!("cmd size: {:#x}, cmd num: {:#x}", cmd_size, cmd_num); let mut vm_cmdq_addr = zone_addr + origin_readr; let mut real_cmdq_addr = self.phy_addr + self.readr; @@ -342,10 +375,9 @@ impl Cmdq { loop { self.readr = (ptr::read_volatile(readr as *mut u64)) as usize; // hw readr if self.readr == self.writer { - trace!( + debug!( "readr={:#x}, writer={:#x}, its cmd end", - self.readr, - self.writer + self.readr, self.writer ); break; } else { diff --git a/src/device/irqchip/gicv3/vgic.rs b/src/device/irqchip/gicv3/vgic.rs index b5662b4d..7c52c26e 100644 --- a/src/device/irqchip/gicv3/vgic.rs +++ b/src/device/irqchip/gicv3/vgic.rs @@ -338,86 +338,79 @@ pub fn vgicv3_its_handler(mmio: &mut MMIOAccess, _arg: usize) -> HvResult { match reg { GITS_CTRL => { mmio_perform_access(gits_base, mmio); - if mmio.is_write { - trace!("write GITS_CTRL: {:#x}", mmio.value); - } else { - trace!("read GITS_CTRL: {:#x}", mmio.value); - } } GITS_CBASER => { if mmio.is_write { - if zone_id == 0 { - mmio_perform_access(gits_base, mmio); - } set_cbaser(mmio.value, zone_id); - trace!("write GITS_CBASER: {:#x}", mmio.value); } else { mmio.value = read_cbaser(zone_id); - trace!("read GITS_CBASER: {:#x}", mmio.value); } } + // v_dt_addr + 0x10000000; GITS_BASER => { - if zone_id == 0 { - mmio_perform_access(gits_base, mmio); - } else { - if mmio.is_write { - set_dt_baser(mmio.value, zone_id); - } else { - mmio.value = read_dt_baser(zone_id); - } - } if mmio.is_write { - trace!("write GITS_BASER: 0x{:016x}", mmio.value); + set_dt_baser(mmio.value, zone_id); + if zone_id == 0 { + let v_dt_addr = mmio.value & 0xfff_fff_fff_000usize; + let phys_dt_trans = + unsafe { this_zone().read().gpm.page_table_query(v_dt_addr) }; + match phys_dt_trans { + Ok(p) => { + mmio.value &= !0xfff_fff_fff_000usize; + mmio.value |= p.0 as usize; + } + _ => {} + } + mmio_perform_access(gits_base, mmio); + } } else { - trace!("read GITS_BASER: 0x{:016x}", mmio.value); + mmio.value = read_dt_baser(zone_id); } } GITS_COLLECTION_BASER => { - if zone_id == 0 { - mmio_perform_access(gits_base, mmio); - } else { - if mmio.is_write { - set_ct_baser(mmio.value, zone_id); - } else { - mmio.value = read_ct_baser(zone_id); - } - } if mmio.is_write { - trace!("write GITS_COLL_BASER: 0x{:016x}", mmio.value); + set_ct_baser(mmio.value, zone_id); + if zone_id == 0 { + let v_ct_addr = mmio.value & 0xfff_fff_fff_000usize; + let phys_ct_trans = + unsafe { this_zone().read().gpm.page_table_query(v_ct_addr) }; + match phys_ct_trans { + Ok(p) => { + mmio.value &= !0xfff_fff_fff_000usize; + mmio.value |= p.0 as usize; + } + _ => {} + } + mmio_perform_access(gits_base, mmio); + } } else { - trace!("read GITS_COLL_BASER: 0x{:016x}", mmio.value); + mmio.value = read_ct_baser(zone_id); } } GITS_CWRITER => { if mmio.is_write { - trace!("write GITS_CWRITER: {:#x}", mmio.value); set_cwriter(mmio.value, zone_id); } else { mmio.value = read_cwriter(zone_id); - trace!("read GITS_CWRITER: {:#x}", mmio.value); } } GITS_CREADR => { mmio.value = read_creadr(zone_id); - trace!("read GITS_CREADER: {:#x}", mmio.value); } GITS_TYPER => { mmio_perform_access(gits_base, mmio); - trace!("GITS_TYPER: {:#x}", mmio.value); } _ => { mmio_perform_access(gits_base, mmio); if mmio.is_write { - trace!( + debug!( "write GITS offset: {:#x}, 0x{:016x}", - mmio.address, - mmio.value + mmio.address, mmio.value ); } else { - trace!( + debug!( "read GITS offset: {:#x}, 0x{:016x}", - mmio.address, - mmio.value + mmio.address, mmio.value ); } } diff --git a/src/pci/pci.rs b/src/pci/pci.rs index ac0d5ec7..93d50fe7 100644 --- a/src/pci/pci.rs +++ b/src/pci/pci.rs @@ -185,8 +185,13 @@ impl Zone { alloc_pci_devs[idx] & 0b111 ); self.pciroot.alloc_devs.push(alloc_pci_devs[idx] as _); + #[cfg(all(feature = "iommu", target_arch = "aarch64"))] if alloc_pci_devs[idx] != 0 { - iommu_add_device(self.id, alloc_pci_devs[idx] as _); + iommu_add_device( + self.id, + alloc_pci_devs[idx] as _, + self.iommu_pt.as_ref().unwrap().root_paddr(), + ); } } diff --git a/src/zone.rs b/src/zone.rs index 5e6483e1..0ede1517 100644 --- a/src/zone.rs +++ b/src/zone.rs @@ -39,6 +39,7 @@ pub struct Zone { pub irq_bitmap: [u32; 1024 / 32], pub gpm: MemorySet, pub pciroot: PciRoot, + pub iommu_pt: Option>, pub is_err: bool, } @@ -53,6 +54,11 @@ impl Zone { mmio: Vec::new(), irq_bitmap: [0; 1024 / 32], pciroot: PciRoot::new(), + iommu_pt: if cfg!(feature = "iommu") { + Some(new_s2_memory_set()) + } else { + None + }, is_err: false, } } @@ -208,6 +214,10 @@ pub fn zone_create(config: &HvZoneConfig) -> HvResult>> { // #[cfg(target_arch = "aarch64")] // zone.ivc_init(config.ivc_config()); + #[cfg(all(feature = "iommu", target_arch = "aarch64"))] + zone.iommu_pt_init(config.memory_regions(), &config.arch_config) + .unwrap(); + /* loongarch page table emergency */ /* Kai: Maybe unnecessary but i can't boot vms on my 3A6000 PC without this function. */ // #[cfg(target_arch = "loongarch64")]