From: Nikita Kalyazin Let's provide folio_{zap,restore}_direct_map helpers as preparation for supporting removal of the direct map for guest_memfd folios. In folio_zap_direct_map(), flush TLB to make sure the data is not accessible. On some architectures, there may be a double TLB flush issued because set_direct_map_valid_noflush already performs a flush internally. The new helpers need to be accessible to KVM on architectures that support guest_memfd (x86 and arm64). Direct map removal gives guest_memfd the same protection that memfd_secret does, such as hardening against Spectre-like attacks through in-kernel gadgets. Acked-by: David Hildenbrand (Arm) Signed-off-by: Nikita Kalyazin [Added comment, dropped modified set_direct_map API, added highmem check] Signed-off-by: Brendan Jackman --- include/linux/set_memory.h | 13 +++++++++++++ mm/memory.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/include/linux/set_memory.h b/include/linux/set_memory.h index 3030d9245f5ac..1bf2a15bca118 100644 --- a/include/linux/set_memory.h +++ b/include/linux/set_memory.h @@ -40,6 +40,15 @@ static inline int set_direct_map_valid_noflush(struct page *page, return 0; } +static inline int folio_zap_direct_map(struct folio *folio) +{ + return 0; +} + +static inline void folio_restore_direct_map(struct folio *folio) +{ +} + static inline bool kernel_page_present(struct page *page) { return true; @@ -56,6 +65,10 @@ static inline bool can_set_direct_map(void) } #define can_set_direct_map can_set_direct_map #endif + +int folio_zap_direct_map(struct folio *folio); +void folio_restore_direct_map(struct folio *folio); + #endif /* CONFIG_ARCH_HAS_SET_DIRECT_MAP */ #ifdef CONFIG_X86_64 diff --git a/mm/memory.c b/mm/memory.c index a73af1fccb3d0..789c65a6d6a0e 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -78,6 +78,7 @@ #include #include #include +#include #include @@ -7758,3 +7759,48 @@ void vma_pgtable_walk_end(struct vm_area_struct *vma) if (is_vm_hugetlb_page(vma)) hugetlb_vma_unlock_read(vma); } + +#ifdef CONFIG_ARCH_HAS_SET_DIRECT_MAP +/** + * folio_zap_direct_map - remove a folio from the kernel direct map + * @folio: folio to remove from the direct map + * + * Removes the folio from the kernel direct map and flushes the TLB. This may + * require splitting huge pages in the direct map, which can fail due to memory + * allocation. So far, only order-0 folios are supported; this guarantees + * the unmap is either a complete success or a total failure. + * + * Return: 0 on success, or a negative error code on failure. + */ +int folio_zap_direct_map(struct folio *folio) +{ + struct page *page = folio_page(folio, 0); + unsigned long addr = (unsigned long)page_address(page); + int ret; + + if (folio_test_large(folio) || folio_test_highmem(folio)) + return -EINVAL; + + ret = set_direct_map_valid_noflush(page, 1, false); + flush_tlb_kernel_range(addr, addr + folio_size(folio)); + + return ret; +} +EXPORT_SYMBOL_FOR_MODULES(folio_zap_direct_map, "kvm"); + +/** + * folio_restore_direct_map - restore the kernel direct map entry for a folio + * @folio: folio whose direct map entry is to be restored + * + * This may only be called after a prior successful folio_zap_direct_map() on + * the same folio. Because the zap will have already split any huge pages in + * the direct map, restoration here only updates protection bits and cannot + * fail. + */ +void folio_restore_direct_map(struct folio *folio) +{ + WARN_ON_ONCE(set_direct_map_valid_noflush(folio_page(folio, 0), + folio_nr_pages(folio), true)); +} +EXPORT_SYMBOL_FOR_MODULES(folio_restore_direct_map, "kvm"); +#endif /* CONFIG_ARCH_HAS_SET_DIRECT_MAP */ -- 2.54.0