diff --git a/arch/arm64/kvm/hyp/include/nvhe/memory.h b/arch/arm64/kvm/hyp/include/nvhe/memory.h index cccef45019d0..d7572a62640a 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/memory.h +++ b/arch/arm64/kvm/hyp/include/nvhe/memory.h @@ -14,13 +14,13 @@ * 01: The page is owned by the page-table owner, but is shared * with another entity. * 10: The page is shared with, but not owned by the page-table owner. - * 11: Reserved for future use (lending). + * 11: The page is tainted by host, and can't transition. */ enum pkvm_page_state { PKVM_PAGE_OWNED = 0ULL, PKVM_PAGE_SHARED_OWNED = BIT(0), PKVM_PAGE_SHARED_BORROWED = BIT(1), - __PKVM_PAGE_RESERVED = BIT(0) | BIT(1), + PKVM_PAGE_TAINTED = BIT(0) | BIT(1), /* Special non-meta state that only applies to host pages. Will not go in PTE SW bits. */ PKVM_MODULE_OWNED_PAGE = BIT(2), diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 1d3ee3b65e4b..c4c3e24a9f2c 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -1747,12 +1747,37 @@ int __pkvm_host_use_dma(phys_addr_t phys_addr, size_t size) return -EINVAL; host_lock_component(); - ret = ___host_check_page_state_range(phys_addr, size, PKVM_PAGE_OWNED, reg, false); - if (ret || !reg) - goto out_ret; + /* + * Some differences between handling of RAM and device memory: + * - The hyp vmemmap area for device memory is not backed by physical + * pages in the hyp page tables. + * - However, in some cases modules can donate MMIO, as they can't be + * refcounted, taint them by marking them as shared PKVM_PAGE_TAINTED, and that + * will prevent any future transition. + */ + if (!reg) { + enum kvm_pgtable_prot prot; - for (i = 0; i < nr_pages; i++) - __pkvm_host_use_dma_page(phys_addr + i * PAGE_SIZE); + ret = ___host_check_page_state_range(phys_addr, size, + PKVM_PAGE_TAINTED, + reg, false); + if (!ret) + goto out_ret; + ret = ___host_check_page_state_range(phys_addr, size, + PKVM_PAGE_OWNED, + reg, false); + if (ret) + goto out_ret; + prot = pkvm_mkstate(PKVM_HOST_MMIO_PROT, PKVM_PAGE_TAINTED); + ret = host_stage2_idmap_locked(phys_addr, size, prot, false); + } else { + ret = ___host_check_page_state_range(phys_addr, size, PKVM_PAGE_OWNED, reg, false); + if (ret) + goto out_ret; + + for (i = 0; i < nr_pages; i++) + __pkvm_host_use_dma_page(phys_addr + i * PAGE_SIZE); + } out_ret: host_unlock_component();