Commit 214d9bbc authored by Claudio Imbrenda's avatar Claudio Imbrenda Committed by Christian Borntraeger
Browse files

s390/mm: provide memory management functions for protected KVM guests



This provides the basic ultravisor calls and page table handling to cope
with secure guests:
- provide arch_make_page_accessible
- make pages accessible after unmapping of secure guests
- provide the ultravisor commands convert to/from secure
- provide the ultravisor commands pin/unpin shared
- provide callbacks to make pages secure (inacccessible)
 - we check for the expected pin count to only make pages secure if the
   host is not accessing them
 - we fence hugetlbfs for secure pages
- add missing radix-tree include into gmap.h

The basic idea is that a page can have 3 states: secure, normal or
shared. The hypervisor can call into a firmware function called
ultravisor that allows to change the state of a page: convert from/to
secure. The convert from secure will encrypt the page and make it
available to the host and host I/O. The convert to secure will remove
the host capability to access this page.
The design is that on convert to secure we will wait until writeback and
page refs are indicating no host usage. At the same time the convert
from secure (export to host) will be called in common code when the
refcount or the writeback bit is already set. This avoids races between
convert from and to secure.

Then there is also the concept of shared pages. Those are kind of secure
where the host can still access those pages. We need to be notified when
the guest "unshares" such a page, basically doing a convert to secure by
then. There is a call "pin shared page" that we use instead of convert
from secure when possible.

We do use PG_arch_1 as an optimization to minimize the convert from
secure/pin shared.

Several comments have been added in the code to explain the logic in
the relevant places.

Co-developed-by: default avatarUlrich Weigand <Ulrich.Weigand@de.ibm.com>
Signed-off-by: default avatarUlrich Weigand <Ulrich.Weigand@de.ibm.com>
Signed-off-by: default avatarClaudio Imbrenda <imbrenda@linux.ibm.com>
Acked-by: default avatarDavid Hildenbrand <david@redhat.com>
Acked-by: default avatarCornelia Huck <cohuck@redhat.com>
Reviewed-by: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
[borntraeger@de.ibm.com: patch merging, splitting, fixing]
Signed-off-by: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
parent 29d37e5b
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#ifndef _ASM_S390_GMAP_H
#define _ASM_S390_GMAP_H

#include <linux/radix-tree.h>
#include <linux/refcount.h>

/* Generic bits for GMAP notification on DAT table entry changes. */
@@ -31,6 +32,7 @@
 * @table: pointer to the page directory
 * @asce: address space control element for gmap page table
 * @pfault_enabled: defines if pfaults are applicable for the guest
 * @guest_handle: protected virtual machine handle for the ultravisor
 * @host_to_rmap: radix tree with gmap_rmap lists
 * @children: list of shadow gmap structures
 * @pt_list: list of all page tables used in the shadow guest address space
@@ -54,6 +56,8 @@ struct gmap {
	unsigned long asce_end;
	void *private;
	bool pfault_enabled;
	/* only set for protected virtual machines */
	unsigned long guest_handle;
	/* Additional data for shadow guest address spaces */
	struct radix_tree_root host_to_rmap;
	struct list_head children;
+2 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@ typedef struct {
	unsigned long asce;
	unsigned long asce_limit;
	unsigned long vdso_base;
	/* The mmu context belongs to a secure guest. */
	atomic_t is_protected;
	/*
	 * The following bitfields need a down_write on the mm
	 * semaphore when they are written to. As they are only
+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ static inline int init_new_context(struct task_struct *tsk,
	INIT_LIST_HEAD(&mm->context.gmap_list);
	cpumask_clear(&mm->context.cpu_attach_mask);
	atomic_set(&mm->context.flush_count, 0);
	atomic_set(&mm->context.is_protected, 0);
	mm->context.gmap_asce = 0;
	mm->context.flush_mm = 0;
	mm->context.compat_mm = test_thread_flag(TIF_31BIT);
+5 −0
Original line number Diff line number Diff line
@@ -153,6 +153,11 @@ static inline int devmem_is_allowed(unsigned long pfn)
#define HAVE_ARCH_FREE_PAGE
#define HAVE_ARCH_ALLOC_PAGE

#if IS_ENABLED(CONFIG_PGSTE)
int arch_make_page_accessible(struct page *page);
#define HAVE_ARCH_MAKE_PAGE_ACCESSIBLE
#endif

#endif /* !__ASSEMBLY__ */

#define __PAGE_OFFSET		0x0UL
+30 −5
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <linux/atomic.h>
#include <asm/bug.h>
#include <asm/page.h>
#include <asm/uv.h>

extern pgd_t swapper_pg_dir[];
extern void paging_init(void);
@@ -520,6 +521,15 @@ static inline int mm_has_pgste(struct mm_struct *mm)
	return 0;
}

static inline int mm_is_protected(struct mm_struct *mm)
{
#ifdef CONFIG_PGSTE
	if (unlikely(atomic_read(&mm->context.is_protected)))
		return 1;
#endif
	return 0;
}

static inline int mm_alloc_pgste(struct mm_struct *mm)
{
#ifdef CONFIG_PGSTE
@@ -1061,7 +1071,12 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
				       unsigned long addr, pte_t *ptep)
{
	return ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID));
	pte_t res;

	res = ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID));
	if (mm_is_protected(mm) && pte_present(res))
		uv_convert_from_secure(pte_val(res) & PAGE_MASK);
	return res;
}

#define __HAVE_ARCH_PTEP_MODIFY_PROT_TRANSACTION
@@ -1073,7 +1088,12 @@ void ptep_modify_prot_commit(struct vm_area_struct *, unsigned long,
static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
				     unsigned long addr, pte_t *ptep)
{
	return ptep_xchg_direct(vma->vm_mm, addr, ptep, __pte(_PAGE_INVALID));
	pte_t res;

	res = ptep_xchg_direct(vma->vm_mm, addr, ptep, __pte(_PAGE_INVALID));
	if (mm_is_protected(vma->vm_mm) && pte_present(res))
		uv_convert_from_secure(pte_val(res) & PAGE_MASK);
	return res;
}

/*
@@ -1088,12 +1108,17 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
					    unsigned long addr,
					    pte_t *ptep, int full)
{
	pte_t res;

	if (full) {
		pte_t pte = *ptep;
		res = *ptep;
		*ptep = __pte(_PAGE_INVALID);
		return pte;
	} else {
		res = ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID));
	}
	return ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID));
	if (mm_is_protected(mm) && pte_present(res))
		uv_convert_from_secure(pte_val(res) & PAGE_MASK);
	return res;
}

#define __HAVE_ARCH_PTEP_SET_WRPROTECT
Loading