[PATCH, RFC 44/62] x86/mm: Set KeyIDs in encrypted VMAs for MKTME

From: Kirill A. Shutemov
Date: Wed May 08 2019 - 10:46:46 EST


From: Alison Schofield <alison.schofield@xxxxxxxxx>

MKTME architecture requires the KeyID to be placed in PTE bits 51:46.
To create an encrypted VMA, place the KeyID in the upper bits of
vm_page_prot that matches the position of those PTE bits.

When the VMA is assigned a KeyID it is always considered a KeyID
change. The VMA is either going from not encrypted to encrypted,
or from encrypted with any KeyID to encrypted with any other KeyID.
To make the change safely, remove the user pages held by the VMA
and unlink the VMA's anonymous chain.

Signed-off-by: Alison Schofield <alison.schofield@xxxxxxxxx>
Signed-off-by: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx>
---
arch/x86/include/asm/mktme.h | 4 ++++
arch/x86/mm/mktme.c | 26 ++++++++++++++++++++++++++
include/linux/mm.h | 6 ++++++
3 files changed, 36 insertions(+)

diff --git a/arch/x86/include/asm/mktme.h b/arch/x86/include/asm/mktme.h
index bd6707e73219..0e6df07f1921 100644
--- a/arch/x86/include/asm/mktme.h
+++ b/arch/x86/include/asm/mktme.h
@@ -12,6 +12,10 @@ extern phys_addr_t mktme_keyid_mask;
extern int mktme_nr_keyids;
extern int mktme_keyid_shift;

+/* Set the encryption keyid bits in a VMA */
+extern void mprotect_set_encrypt(struct vm_area_struct *vma, int newkeyid,
+ unsigned long start, unsigned long end);
+
DECLARE_STATIC_KEY_FALSE(mktme_enabled_key);
static inline bool mktme_enabled(void)
{
diff --git a/arch/x86/mm/mktme.c b/arch/x86/mm/mktme.c
index 024165c9c7f3..91b49e88ca3f 100644
--- a/arch/x86/mm/mktme.c
+++ b/arch/x86/mm/mktme.c
@@ -1,5 +1,6 @@
#include <linux/mm.h>
#include <linux/highmem.h>
+#include <linux/rmap.h>
#include <asm/mktme.h>
#include <asm/pgalloc.h>
#include <asm/tlbflush.h>
@@ -53,6 +54,31 @@ int __vma_keyid(struct vm_area_struct *vma)
return (prot & mktme_keyid_mask) >> mktme_keyid_shift;
}

+/* Set the encryption keyid bits in a VMA */
+void mprotect_set_encrypt(struct vm_area_struct *vma, int newkeyid,
+ unsigned long start, unsigned long end)
+{
+ int oldkeyid = vma_keyid(vma);
+ pgprotval_t newprot;
+
+ /* Unmap pages with old KeyID if there's any. */
+ zap_page_range(vma, start, end - start);
+
+ if (oldkeyid == newkeyid)
+ return;
+
+ newprot = pgprot_val(vma->vm_page_prot);
+ newprot &= ~mktme_keyid_mask;
+ newprot |= (unsigned long)newkeyid << mktme_keyid_shift;
+ vma->vm_page_prot = __pgprot(newprot);
+
+ /*
+ * The VMA doesn't have any inherited pages.
+ * Start anon VMA tree from scratch.
+ */
+ unlink_anon_vmas(vma);
+}
+
/* Prepare page to be used for encryption. Called from page allocator. */
void __prep_encrypted_page(struct page *page, int order, int keyid, bool zero)
{
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 2684245f8503..c027044de9bf 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2825,5 +2825,11 @@ void __init setup_nr_node_ids(void);
static inline void setup_nr_node_ids(void) {}
#endif

+#ifndef CONFIG_X86_INTEL_MKTME
+static inline void mprotect_set_encrypt(struct vm_area_struct *vma,
+ int newkeyid,
+ unsigned long start,
+ unsigned long end) {}
+#endif /* CONFIG_X86_INTEL_MKTME */
#endif /* __KERNEL__ */
#endif /* _LINUX_MM_H */
--
2.20.1