mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'kvm-s390-next-7.0-1' of https://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into HEAD
- gmap rewrite: completely new memory management for kvm/s390 - vSIE improvement - maintainership change for s390 vfio-pci - small quality of life improvement for protected guests
This commit is contained in:
@@ -6518,6 +6518,40 @@ the capability to be present.
|
||||
|
||||
`flags` must currently be zero.
|
||||
|
||||
4.144 KVM_S390_KEYOP
|
||||
--------------------
|
||||
|
||||
:Capability: KVM_CAP_S390_KEYOP
|
||||
:Architectures: s390
|
||||
:Type: vm ioctl
|
||||
:Parameters: struct kvm_s390_keyop (in/out)
|
||||
:Returns: 0 in case of success, < 0 on error
|
||||
|
||||
The specified key operation is performed on the given guest address. The
|
||||
previous storage key (or the relevant part thereof) will be returned in
|
||||
`key`.
|
||||
|
||||
::
|
||||
|
||||
struct kvm_s390_keyop {
|
||||
__u64 guest_addr;
|
||||
__u8 key;
|
||||
__u8 operation;
|
||||
};
|
||||
|
||||
Currently supported values for ``operation``:
|
||||
|
||||
KVM_S390_KEYOP_ISKE
|
||||
Returns the storage key for the guest address ``guest_addr`` in ``key``.
|
||||
|
||||
KVM_S390_KEYOP_RRBE
|
||||
Resets the reference bit for the guest address ``guest_addr``, returning the
|
||||
R and C bits of the old storage key in ``key``; the remaining fields of
|
||||
the storage key will be set to 0.
|
||||
|
||||
KVM_S390_KEYOP_SSKE
|
||||
Sets the storage key for the guest address ``guest_addr`` to the key
|
||||
specified in ``key``, returning the previous value in ``key``.
|
||||
|
||||
.. _kvm_run:
|
||||
|
||||
@@ -9384,6 +9418,14 @@ The presence of this capability indicates that KVM_RUN will update the
|
||||
KVM_RUN_X86_GUEST_MODE bit in kvm_run.flags to indicate whether the
|
||||
vCPU was executing nested guest code when it exited.
|
||||
|
||||
8.46 KVM_CAP_S390_KEYOP
|
||||
-----------------------
|
||||
|
||||
:Architectures: s390
|
||||
|
||||
The presence of this capability indicates that the KVM_S390_KEYOP ioctl is
|
||||
available.
|
||||
|
||||
KVM exits with the register state of either the L1 or L2 guest
|
||||
depending on which executed at the time of an exit. Userspace must
|
||||
take care to differentiate between these cases.
|
||||
|
||||
+2
-3
@@ -13914,14 +13914,12 @@ L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux.git
|
||||
F: Documentation/virt/kvm/s390*
|
||||
F: arch/s390/include/asm/gmap.h
|
||||
F: arch/s390/include/asm/gmap_helpers.h
|
||||
F: arch/s390/include/asm/kvm*
|
||||
F: arch/s390/include/uapi/asm/kvm*
|
||||
F: arch/s390/include/uapi/asm/uvdevice.h
|
||||
F: arch/s390/kernel/uv.c
|
||||
F: arch/s390/kvm/
|
||||
F: arch/s390/mm/gmap.c
|
||||
F: arch/s390/mm/gmap_helpers.c
|
||||
F: drivers/s390/char/uvdevice.c
|
||||
F: tools/testing/selftests/drivers/s390x/uvdevice/
|
||||
@@ -23111,7 +23109,8 @@ F: include/uapi/linux/vfio_ccw.h
|
||||
|
||||
S390 VFIO-PCI DRIVER
|
||||
M: Matthew Rosato <mjrosato@linux.ibm.com>
|
||||
M: Eric Farman <farman@linux.ibm.com>
|
||||
M: Farhan Ali <alifm@linux.ibm.com>
|
||||
R: Eric Farman <farman@linux.ibm.com>
|
||||
L: linux-s390@vger.kernel.org
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
|
||||
@@ -32,9 +32,6 @@ config GENERIC_BUG_RELATIVE_POINTERS
|
||||
config GENERIC_LOCKBREAK
|
||||
def_bool y if PREEMPTION
|
||||
|
||||
config PGSTE
|
||||
def_bool y if KVM
|
||||
|
||||
config AUDIT_ARCH
|
||||
def_bool y
|
||||
|
||||
|
||||
@@ -9,6 +9,32 @@
|
||||
#ifndef _S390_DAT_BITS_H
|
||||
#define _S390_DAT_BITS_H
|
||||
|
||||
/*
|
||||
* vaddress union in order to easily decode a virtual address into its
|
||||
* region first index, region second index etc. parts.
|
||||
*/
|
||||
union vaddress {
|
||||
unsigned long addr;
|
||||
struct {
|
||||
unsigned long rfx : 11;
|
||||
unsigned long rsx : 11;
|
||||
unsigned long rtx : 11;
|
||||
unsigned long sx : 11;
|
||||
unsigned long px : 8;
|
||||
unsigned long bx : 12;
|
||||
};
|
||||
struct {
|
||||
unsigned long rfx01 : 2;
|
||||
unsigned long : 9;
|
||||
unsigned long rsx01 : 2;
|
||||
unsigned long : 9;
|
||||
unsigned long rtx01 : 2;
|
||||
unsigned long : 9;
|
||||
unsigned long sx01 : 2;
|
||||
unsigned long : 29;
|
||||
};
|
||||
};
|
||||
|
||||
union asce {
|
||||
unsigned long val;
|
||||
struct {
|
||||
@@ -98,7 +124,8 @@ union region3_table_entry {
|
||||
struct {
|
||||
unsigned long : 53;
|
||||
unsigned long fc: 1; /* Format-Control */
|
||||
unsigned long : 4;
|
||||
unsigned long p : 1; /* DAT-Protection Bit */
|
||||
unsigned long : 3;
|
||||
unsigned long i : 1; /* Region-Invalid Bit */
|
||||
unsigned long cr: 1; /* Common-Region Bit */
|
||||
unsigned long tt: 2; /* Table-Type Bits */
|
||||
@@ -140,7 +167,8 @@ union segment_table_entry {
|
||||
struct {
|
||||
unsigned long : 53;
|
||||
unsigned long fc: 1; /* Format-Control */
|
||||
unsigned long : 4;
|
||||
unsigned long p : 1; /* DAT-Protection Bit */
|
||||
unsigned long : 3;
|
||||
unsigned long i : 1; /* Segment-Invalid Bit */
|
||||
unsigned long cs: 1; /* Common-Segment Bit */
|
||||
unsigned long tt: 2; /* Table-Type Bits */
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* KVM guest address space mapping code
|
||||
*
|
||||
* Copyright IBM Corp. 2007, 2016
|
||||
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
|
||||
*/
|
||||
|
||||
#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. */
|
||||
#define GMAP_NOTIFY_SHADOW 0x2
|
||||
#define GMAP_NOTIFY_MPROT 0x1
|
||||
|
||||
/* Status bits only for huge segment entries */
|
||||
#define _SEGMENT_ENTRY_GMAP_IN 0x0800 /* invalidation notify bit */
|
||||
#define _SEGMENT_ENTRY_GMAP_UC 0x0002 /* dirty (migration) */
|
||||
|
||||
/**
|
||||
* struct gmap_struct - guest address space
|
||||
* @list: list head for the mm->context gmap list
|
||||
* @mm: pointer to the parent mm_struct
|
||||
* @guest_to_host: radix tree with guest to host address translation
|
||||
* @host_to_guest: radix tree with pointer to segment table entries
|
||||
* @guest_table_lock: spinlock to protect all entries in the guest page table
|
||||
* @ref_count: reference counter for the gmap structure
|
||||
* @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
|
||||
* @shadow_lock: spinlock to protect the shadow gmap list
|
||||
* @parent: pointer to the parent gmap for shadow guest address spaces
|
||||
* @orig_asce: ASCE for which the shadow page table has been created
|
||||
* @edat_level: edat level to be used for the shadow translation
|
||||
* @removed: flag to indicate if a shadow guest address space has been removed
|
||||
* @initialized: flag to indicate if a shadow guest address space can be used
|
||||
*/
|
||||
struct gmap {
|
||||
struct list_head list;
|
||||
struct mm_struct *mm;
|
||||
struct radix_tree_root guest_to_host;
|
||||
struct radix_tree_root host_to_guest;
|
||||
spinlock_t guest_table_lock;
|
||||
refcount_t ref_count;
|
||||
unsigned long *table;
|
||||
unsigned long asce;
|
||||
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;
|
||||
spinlock_t shadow_lock;
|
||||
struct gmap *parent;
|
||||
unsigned long orig_asce;
|
||||
int edat_level;
|
||||
bool removed;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gmap_rmap - reverse mapping for shadow page table entries
|
||||
* @next: pointer to next rmap in the list
|
||||
* @raddr: virtual rmap address in the shadow guest address space
|
||||
*/
|
||||
struct gmap_rmap {
|
||||
struct gmap_rmap *next;
|
||||
unsigned long raddr;
|
||||
};
|
||||
|
||||
#define gmap_for_each_rmap(pos, head) \
|
||||
for (pos = (head); pos; pos = pos->next)
|
||||
|
||||
#define gmap_for_each_rmap_safe(pos, n, head) \
|
||||
for (pos = (head); n = pos ? pos->next : NULL, pos; pos = n)
|
||||
|
||||
/**
|
||||
* struct gmap_notifier - notify function block for page invalidation
|
||||
* @notifier_call: address of callback function
|
||||
*/
|
||||
struct gmap_notifier {
|
||||
struct list_head list;
|
||||
struct rcu_head rcu;
|
||||
void (*notifier_call)(struct gmap *gmap, unsigned long start,
|
||||
unsigned long end);
|
||||
};
|
||||
|
||||
static inline int gmap_is_shadow(struct gmap *gmap)
|
||||
{
|
||||
return !!gmap->parent;
|
||||
}
|
||||
|
||||
struct gmap *gmap_create(struct mm_struct *mm, unsigned long limit);
|
||||
void gmap_remove(struct gmap *gmap);
|
||||
struct gmap *gmap_get(struct gmap *gmap);
|
||||
void gmap_put(struct gmap *gmap);
|
||||
void gmap_free(struct gmap *gmap);
|
||||
struct gmap *gmap_alloc(unsigned long limit);
|
||||
|
||||
int gmap_map_segment(struct gmap *gmap, unsigned long from,
|
||||
unsigned long to, unsigned long len);
|
||||
int gmap_unmap_segment(struct gmap *gmap, unsigned long to, unsigned long len);
|
||||
unsigned long __gmap_translate(struct gmap *, unsigned long gaddr);
|
||||
int __gmap_link(struct gmap *gmap, unsigned long gaddr, unsigned long vmaddr);
|
||||
void __gmap_zap(struct gmap *, unsigned long gaddr);
|
||||
void gmap_unlink(struct mm_struct *, unsigned long *table, unsigned long vmaddr);
|
||||
|
||||
int gmap_read_table(struct gmap *gmap, unsigned long gaddr, unsigned long *val);
|
||||
|
||||
void gmap_unshadow(struct gmap *sg);
|
||||
int gmap_shadow_r2t(struct gmap *sg, unsigned long saddr, unsigned long r2t,
|
||||
int fake);
|
||||
int gmap_shadow_r3t(struct gmap *sg, unsigned long saddr, unsigned long r3t,
|
||||
int fake);
|
||||
int gmap_shadow_sgt(struct gmap *sg, unsigned long saddr, unsigned long sgt,
|
||||
int fake);
|
||||
int gmap_shadow_pgt(struct gmap *sg, unsigned long saddr, unsigned long pgt,
|
||||
int fake);
|
||||
int gmap_shadow_page(struct gmap *sg, unsigned long saddr, pte_t pte);
|
||||
|
||||
void gmap_register_pte_notifier(struct gmap_notifier *);
|
||||
void gmap_unregister_pte_notifier(struct gmap_notifier *);
|
||||
|
||||
int gmap_protect_one(struct gmap *gmap, unsigned long gaddr, int prot, unsigned long bits);
|
||||
|
||||
void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long dirty_bitmap[4],
|
||||
unsigned long gaddr, unsigned long vmaddr);
|
||||
int s390_replace_asce(struct gmap *gmap);
|
||||
void s390_uv_destroy_pfns(unsigned long count, unsigned long *pfns);
|
||||
int __s390_uv_destroy_range(struct mm_struct *mm, unsigned long start,
|
||||
unsigned long end, bool interruptible);
|
||||
unsigned long *gmap_table_walk(struct gmap *gmap, unsigned long gaddr, int level);
|
||||
|
||||
/**
|
||||
* s390_uv_destroy_range - Destroy a range of pages in the given mm.
|
||||
* @mm: the mm on which to operate on
|
||||
* @start: the start of the range
|
||||
* @end: the end of the range
|
||||
*
|
||||
* This function will call cond_sched, so it should not generate stalls, but
|
||||
* it will otherwise only return when it completed.
|
||||
*/
|
||||
static inline void s390_uv_destroy_range(struct mm_struct *mm, unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
(void)__s390_uv_destroy_range(mm, start, end, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* s390_uv_destroy_range_interruptible - Destroy a range of pages in the
|
||||
* given mm, but stop when a fatal signal is received.
|
||||
* @mm: the mm on which to operate on
|
||||
* @start: the start of the range
|
||||
* @end: the end of the range
|
||||
*
|
||||
* This function will call cond_sched, so it should not generate stalls. If
|
||||
* a fatal signal is received, it will return with -EINTR immediately,
|
||||
* without finishing destroying the whole range. Upon successful
|
||||
* completion, 0 is returned.
|
||||
*/
|
||||
static inline int s390_uv_destroy_range_interruptible(struct mm_struct *mm, unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
return __s390_uv_destroy_range(mm, start, end, true);
|
||||
}
|
||||
#endif /* _ASM_S390_GMAP_H */
|
||||
@@ -11,5 +11,6 @@
|
||||
void gmap_helper_zap_one_page(struct mm_struct *mm, unsigned long vmaddr);
|
||||
void gmap_helper_discard(struct mm_struct *mm, unsigned long vmaddr, unsigned long end);
|
||||
int gmap_helper_disable_cow_sharing(void);
|
||||
void gmap_helper_try_set_pte_unused(struct mm_struct *mm, unsigned long vmaddr);
|
||||
|
||||
#endif /* _ASM_S390_GMAP_HELPERS_H */
|
||||
|
||||
@@ -37,12 +37,6 @@ static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
|
||||
return __huge_ptep_get_and_clear(mm, addr, ptep);
|
||||
}
|
||||
|
||||
static inline void arch_clear_hugetlb_flags(struct folio *folio)
|
||||
{
|
||||
clear_bit(PG_arch_1, &folio->flags.f);
|
||||
}
|
||||
#define arch_clear_hugetlb_flags arch_clear_hugetlb_flags
|
||||
|
||||
#define __HAVE_ARCH_HUGE_PTE_CLEAR
|
||||
static inline void huge_pte_clear(struct mm_struct *mm, unsigned long addr,
|
||||
pte_t *ptep, unsigned long sz)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <asm/isc.h>
|
||||
#include <asm/guarded_storage.h>
|
||||
|
||||
#define KVM_HAVE_MMU_RWLOCK
|
||||
#define KVM_MAX_VCPUS 255
|
||||
|
||||
#define KVM_INTERNAL_MEM_SLOTS 1
|
||||
@@ -441,6 +442,7 @@ struct kvm_vcpu_arch {
|
||||
bool acrs_loaded;
|
||||
struct kvm_s390_pv_vcpu pv;
|
||||
union diag318_info diag318_info;
|
||||
struct kvm_s390_mmu_cache *mc;
|
||||
};
|
||||
|
||||
struct kvm_vm_stat {
|
||||
@@ -630,8 +632,12 @@ struct kvm_s390_pv {
|
||||
void *set_aside;
|
||||
struct list_head need_cleanup;
|
||||
struct mmu_notifier mmu_notifier;
|
||||
/* Protects against concurrent import-like operations */
|
||||
struct mutex import_lock;
|
||||
};
|
||||
|
||||
struct kvm_s390_mmu_cache;
|
||||
|
||||
struct kvm_arch {
|
||||
struct esca_block *sca;
|
||||
debug_info_t *dbf;
|
||||
@@ -671,6 +677,7 @@ struct kvm_arch {
|
||||
struct kvm_s390_pv pv;
|
||||
struct list_head kzdev_list;
|
||||
spinlock_t kzdev_list_lock;
|
||||
struct kvm_s390_mmu_cache *mc;
|
||||
};
|
||||
|
||||
#define KVM_HVA_ERR_BAD (-1UL)
|
||||
|
||||
@@ -18,24 +18,11 @@ typedef struct {
|
||||
unsigned long vdso_base;
|
||||
/* The mmu context belongs to a secure guest. */
|
||||
atomic_t protected_count;
|
||||
/*
|
||||
* The following bitfields need a down_write on the mm
|
||||
* semaphore when they are written to. As they are only
|
||||
* written once, they can be read without a lock.
|
||||
*/
|
||||
/* The mmu context uses extended page tables. */
|
||||
unsigned int has_pgste:1;
|
||||
/* The mmu context uses storage keys. */
|
||||
unsigned int uses_skeys:1;
|
||||
/* The mmu context uses CMM. */
|
||||
unsigned int uses_cmm:1;
|
||||
/*
|
||||
* The mmu context allows COW-sharing of memory pages (KSM, zeropage).
|
||||
* Note that COW-sharing during fork() is currently always allowed.
|
||||
*/
|
||||
unsigned int allow_cow_sharing:1;
|
||||
/* The gmaps associated with this context are allowed to use huge pages. */
|
||||
unsigned int allow_gmap_hpage_1m:1;
|
||||
} mm_context_t;
|
||||
|
||||
#define INIT_MM_CONTEXT(name) \
|
||||
|
||||
@@ -29,12 +29,8 @@ static inline int init_new_context(struct task_struct *tsk,
|
||||
atomic_set(&mm->context.protected_count, 0);
|
||||
mm->context.gmap_asce = 0;
|
||||
mm->context.flush_mm = 0;
|
||||
#ifdef CONFIG_PGSTE
|
||||
mm->context.has_pgste = 0;
|
||||
mm->context.uses_skeys = 0;
|
||||
mm->context.uses_cmm = 0;
|
||||
#if IS_ENABLED(CONFIG_KVM)
|
||||
mm->context.allow_cow_sharing = 1;
|
||||
mm->context.allow_gmap_hpage_1m = 0;
|
||||
#endif
|
||||
switch (mm->context.asce_limit) {
|
||||
default:
|
||||
|
||||
@@ -78,7 +78,6 @@ static inline void copy_page(void *to, void *from)
|
||||
#ifdef STRICT_MM_TYPECHECKS
|
||||
|
||||
typedef struct { unsigned long pgprot; } pgprot_t;
|
||||
typedef struct { unsigned long pgste; } pgste_t;
|
||||
typedef struct { unsigned long pte; } pte_t;
|
||||
typedef struct { unsigned long pmd; } pmd_t;
|
||||
typedef struct { unsigned long pud; } pud_t;
|
||||
@@ -94,7 +93,6 @@ static __always_inline unsigned long name ## _val(name ## _t name) \
|
||||
#else /* STRICT_MM_TYPECHECKS */
|
||||
|
||||
typedef unsigned long pgprot_t;
|
||||
typedef unsigned long pgste_t;
|
||||
typedef unsigned long pte_t;
|
||||
typedef unsigned long pmd_t;
|
||||
typedef unsigned long pud_t;
|
||||
@@ -110,7 +108,6 @@ static __always_inline unsigned long name ## _val(name ## _t name) \
|
||||
#endif /* STRICT_MM_TYPECHECKS */
|
||||
|
||||
DEFINE_PGVAL_FUNC(pgprot)
|
||||
DEFINE_PGVAL_FUNC(pgste)
|
||||
DEFINE_PGVAL_FUNC(pte)
|
||||
DEFINE_PGVAL_FUNC(pmd)
|
||||
DEFINE_PGVAL_FUNC(pud)
|
||||
@@ -120,7 +117,6 @@ DEFINE_PGVAL_FUNC(pgd)
|
||||
typedef pte_t *pgtable_t;
|
||||
|
||||
#define __pgprot(x) ((pgprot_t) { (x) } )
|
||||
#define __pgste(x) ((pgste_t) { (x) } )
|
||||
#define __pte(x) ((pte_t) { (x) } )
|
||||
#define __pmd(x) ((pmd_t) { (x) } )
|
||||
#define __pud(x) ((pud_t) { (x) } )
|
||||
|
||||
@@ -27,10 +27,6 @@ unsigned long *page_table_alloc_noprof(struct mm_struct *);
|
||||
#define page_table_alloc(...) alloc_hooks(page_table_alloc_noprof(__VA_ARGS__))
|
||||
void page_table_free(struct mm_struct *, unsigned long *);
|
||||
|
||||
struct ptdesc *page_table_alloc_pgste_noprof(struct mm_struct *mm);
|
||||
#define page_table_alloc_pgste(...) alloc_hooks(page_table_alloc_pgste_noprof(__VA_ARGS__))
|
||||
void page_table_free_pgste(struct ptdesc *ptdesc);
|
||||
|
||||
static inline void crst_table_init(unsigned long *crst, unsigned long entry)
|
||||
{
|
||||
memset64((u64 *)crst, entry, _CRST_ENTRIES);
|
||||
|
||||
+21
-150
@@ -413,28 +413,6 @@ void setup_protection_map(void);
|
||||
* SW-bits: y young, d dirty, r read, w write
|
||||
*/
|
||||
|
||||
/* Page status table bits for virtualization */
|
||||
#define PGSTE_ACC_BITS 0xf000000000000000UL
|
||||
#define PGSTE_FP_BIT 0x0800000000000000UL
|
||||
#define PGSTE_PCL_BIT 0x0080000000000000UL
|
||||
#define PGSTE_HR_BIT 0x0040000000000000UL
|
||||
#define PGSTE_HC_BIT 0x0020000000000000UL
|
||||
#define PGSTE_GR_BIT 0x0004000000000000UL
|
||||
#define PGSTE_GC_BIT 0x0002000000000000UL
|
||||
#define PGSTE_ST2_MASK 0x0000ffff00000000UL
|
||||
#define PGSTE_UC_BIT 0x0000000000008000UL /* user dirty (migration) */
|
||||
#define PGSTE_IN_BIT 0x0000000000004000UL /* IPTE notify bit */
|
||||
#define PGSTE_VSIE_BIT 0x0000000000002000UL /* ref'd in a shadow table */
|
||||
|
||||
/* Guest Page State used for virtualization */
|
||||
#define _PGSTE_GPS_ZERO 0x0000000080000000UL
|
||||
#define _PGSTE_GPS_NODAT 0x0000000040000000UL
|
||||
#define _PGSTE_GPS_USAGE_MASK 0x0000000003000000UL
|
||||
#define _PGSTE_GPS_USAGE_STABLE 0x0000000000000000UL
|
||||
#define _PGSTE_GPS_USAGE_UNUSED 0x0000000001000000UL
|
||||
#define _PGSTE_GPS_USAGE_POT_VOLATILE 0x0000000002000000UL
|
||||
#define _PGSTE_GPS_USAGE_VOLATILE _PGSTE_GPS_USAGE_MASK
|
||||
|
||||
/*
|
||||
* A user page table pointer has the space-switch-event bit, the
|
||||
* private-space-control bit and the storage-alteration-event-control
|
||||
@@ -566,34 +544,15 @@ static inline bool mm_pmd_folded(struct mm_struct *mm)
|
||||
}
|
||||
#define mm_pmd_folded(mm) mm_pmd_folded(mm)
|
||||
|
||||
static inline int mm_has_pgste(struct mm_struct *mm)
|
||||
{
|
||||
#ifdef CONFIG_PGSTE
|
||||
if (unlikely(mm->context.has_pgste))
|
||||
return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mm_is_protected(struct mm_struct *mm)
|
||||
{
|
||||
#ifdef CONFIG_PGSTE
|
||||
#if IS_ENABLED(CONFIG_KVM)
|
||||
if (unlikely(atomic_read(&mm->context.protected_count)))
|
||||
return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline pgste_t clear_pgste_bit(pgste_t pgste, unsigned long mask)
|
||||
{
|
||||
return __pgste(pgste_val(pgste) & ~mask);
|
||||
}
|
||||
|
||||
static inline pgste_t set_pgste_bit(pgste_t pgste, unsigned long mask)
|
||||
{
|
||||
return __pgste(pgste_val(pgste) | mask);
|
||||
}
|
||||
|
||||
static inline pte_t clear_pte_bit(pte_t pte, pgprot_t prot)
|
||||
{
|
||||
return __pte(pte_val(pte) & ~pgprot_val(prot));
|
||||
@@ -632,22 +591,13 @@ static inline pud_t set_pud_bit(pud_t pud, pgprot_t prot)
|
||||
#define mm_forbids_zeropage mm_forbids_zeropage
|
||||
static inline int mm_forbids_zeropage(struct mm_struct *mm)
|
||||
{
|
||||
#ifdef CONFIG_PGSTE
|
||||
#if IS_ENABLED(CONFIG_KVM)
|
||||
if (!mm->context.allow_cow_sharing)
|
||||
return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int mm_uses_skeys(struct mm_struct *mm)
|
||||
{
|
||||
#ifdef CONFIG_PGSTE
|
||||
if (mm->context.uses_skeys)
|
||||
return 1;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cspg() - Compare and Swap and Purge (CSPG)
|
||||
* @ptr: Pointer to the value to be exchanged
|
||||
@@ -1136,6 +1086,13 @@ static inline pte_t pte_mkhuge(pte_t pte)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline unsigned long sske_frame(unsigned long addr, unsigned char skey)
|
||||
{
|
||||
asm volatile("sske %[skey],%[addr],1"
|
||||
: [addr] "+a" (addr) : [skey] "d" (skey));
|
||||
return addr;
|
||||
}
|
||||
|
||||
#define IPTE_GLOBAL 0
|
||||
#define IPTE_LOCAL 1
|
||||
|
||||
@@ -1232,7 +1189,7 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
|
||||
res = ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID));
|
||||
/* At this point the reference through the mapping is still present */
|
||||
if (mm_is_protected(mm) && pte_present(res))
|
||||
uv_convert_from_secure_pte(res);
|
||||
WARN_ON_ONCE(uv_convert_from_secure_pte(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1250,7 +1207,7 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
|
||||
res = ptep_xchg_direct(vma->vm_mm, addr, ptep, __pte(_PAGE_INVALID));
|
||||
/* At this point the reference through the mapping is still present */
|
||||
if (mm_is_protected(vma->vm_mm) && pte_present(res))
|
||||
uv_convert_from_secure_pte(res);
|
||||
WARN_ON_ONCE(uv_convert_from_secure_pte(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1287,9 +1244,10 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
|
||||
/*
|
||||
* If something went wrong and the page could not be destroyed, or
|
||||
* if this is not a mm teardown, the slower export is used as
|
||||
* fallback instead.
|
||||
* fallback instead. If even that fails, print a warning and leak
|
||||
* the page, to avoid crashing the whole system.
|
||||
*/
|
||||
uv_convert_from_secure_pte(res);
|
||||
WARN_ON_ONCE(uv_convert_from_secure_pte(res));
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1348,50 +1306,13 @@ static inline int ptep_set_access_flags(struct vm_area_struct *vma,
|
||||
{
|
||||
if (pte_same(*ptep, entry))
|
||||
return 0;
|
||||
if (cpu_has_rdp() && !mm_has_pgste(vma->vm_mm) && pte_allow_rdp(*ptep, entry))
|
||||
if (cpu_has_rdp() && pte_allow_rdp(*ptep, entry))
|
||||
ptep_reset_dat_prot(vma->vm_mm, addr, ptep, entry);
|
||||
else
|
||||
ptep_xchg_direct(vma->vm_mm, addr, ptep, entry);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Additional functions to handle KVM guest page tables
|
||||
*/
|
||||
void ptep_set_pte_at(struct mm_struct *mm, unsigned long addr,
|
||||
pte_t *ptep, pte_t entry);
|
||||
void ptep_set_notify(struct mm_struct *mm, unsigned long addr, pte_t *ptep);
|
||||
void ptep_notify(struct mm_struct *mm, unsigned long addr,
|
||||
pte_t *ptep, unsigned long bits);
|
||||
int ptep_force_prot(struct mm_struct *mm, unsigned long gaddr,
|
||||
pte_t *ptep, int prot, unsigned long bit);
|
||||
void ptep_zap_unused(struct mm_struct *mm, unsigned long addr,
|
||||
pte_t *ptep , int reset);
|
||||
void ptep_zap_key(struct mm_struct *mm, unsigned long addr, pte_t *ptep);
|
||||
int ptep_shadow_pte(struct mm_struct *mm, unsigned long saddr,
|
||||
pte_t *sptep, pte_t *tptep, pte_t pte);
|
||||
void ptep_unshadow_pte(struct mm_struct *mm, unsigned long saddr, pte_t *ptep);
|
||||
|
||||
bool ptep_test_and_clear_uc(struct mm_struct *mm, unsigned long address,
|
||||
pte_t *ptep);
|
||||
int set_guest_storage_key(struct mm_struct *mm, unsigned long addr,
|
||||
unsigned char key, bool nq);
|
||||
int cond_set_guest_storage_key(struct mm_struct *mm, unsigned long addr,
|
||||
unsigned char key, unsigned char *oldkey,
|
||||
bool nq, bool mr, bool mc);
|
||||
int reset_guest_reference_bit(struct mm_struct *mm, unsigned long addr);
|
||||
int get_guest_storage_key(struct mm_struct *mm, unsigned long addr,
|
||||
unsigned char *key);
|
||||
|
||||
int set_pgste_bits(struct mm_struct *mm, unsigned long addr,
|
||||
unsigned long bits, unsigned long value);
|
||||
int get_pgste(struct mm_struct *mm, unsigned long hva, unsigned long *pgstep);
|
||||
int pgste_perform_essa(struct mm_struct *mm, unsigned long hva, int orc,
|
||||
unsigned long *oldpte, unsigned long *oldpgste);
|
||||
void gmap_pmdp_invalidate(struct mm_struct *mm, unsigned long vmaddr);
|
||||
void gmap_pmdp_idte_local(struct mm_struct *mm, unsigned long vmaddr);
|
||||
void gmap_pmdp_idte_global(struct mm_struct *mm, unsigned long vmaddr);
|
||||
|
||||
#define pgprot_writecombine pgprot_writecombine
|
||||
pgprot_t pgprot_writecombine(pgprot_t prot);
|
||||
|
||||
@@ -1406,23 +1327,12 @@ static inline void set_ptes(struct mm_struct *mm, unsigned long addr,
|
||||
{
|
||||
if (pte_present(entry))
|
||||
entry = clear_pte_bit(entry, __pgprot(_PAGE_UNUSED));
|
||||
if (mm_has_pgste(mm)) {
|
||||
for (;;) {
|
||||
ptep_set_pte_at(mm, addr, ptep, entry);
|
||||
if (--nr == 0)
|
||||
break;
|
||||
ptep++;
|
||||
entry = __pte(pte_val(entry) + PAGE_SIZE);
|
||||
addr += PAGE_SIZE;
|
||||
}
|
||||
} else {
|
||||
for (;;) {
|
||||
set_pte(ptep, entry);
|
||||
if (--nr == 0)
|
||||
break;
|
||||
ptep++;
|
||||
entry = __pte(pte_val(entry) + PAGE_SIZE);
|
||||
}
|
||||
for (;;) {
|
||||
set_pte(ptep, entry);
|
||||
if (--nr == 0)
|
||||
break;
|
||||
ptep++;
|
||||
entry = __pte(pte_val(entry) + PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
#define set_ptes set_ptes
|
||||
@@ -2015,9 +1925,6 @@ extern int __vmem_map_4k_page(unsigned long addr, unsigned long phys, pgprot_t p
|
||||
extern int vmem_map_4k_page(unsigned long addr, unsigned long phys, pgprot_t prot);
|
||||
extern void vmem_unmap_4k_page(unsigned long addr);
|
||||
extern pte_t *vmem_get_alloc_pte(unsigned long addr, bool alloc);
|
||||
extern int s390_enable_sie(void);
|
||||
extern int s390_enable_skey(void);
|
||||
extern void s390_reset_cmma(struct mm_struct *mm);
|
||||
|
||||
/* s390 has a private copy of get unmapped area to deal with cache synonyms */
|
||||
#define HAVE_ARCH_UNMAPPED_AREA
|
||||
@@ -2026,40 +1933,4 @@ extern void s390_reset_cmma(struct mm_struct *mm);
|
||||
#define pmd_pgtable(pmd) \
|
||||
((pgtable_t)__va(pmd_val(pmd) & -sizeof(pte_t)*PTRS_PER_PTE))
|
||||
|
||||
static inline unsigned long gmap_pgste_get_pgt_addr(unsigned long *pgt)
|
||||
{
|
||||
unsigned long *pgstes, res;
|
||||
|
||||
pgstes = pgt + _PAGE_ENTRIES;
|
||||
|
||||
res = (pgstes[0] & PGSTE_ST2_MASK) << 16;
|
||||
res |= pgstes[1] & PGSTE_ST2_MASK;
|
||||
res |= (pgstes[2] & PGSTE_ST2_MASK) >> 16;
|
||||
res |= (pgstes[3] & PGSTE_ST2_MASK) >> 32;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline pgste_t pgste_get_lock(pte_t *ptep)
|
||||
{
|
||||
unsigned long value = 0;
|
||||
#ifdef CONFIG_PGSTE
|
||||
unsigned long *ptr = (unsigned long *)(ptep + PTRS_PER_PTE);
|
||||
|
||||
do {
|
||||
value = __atomic64_or_barrier(PGSTE_PCL_BIT, ptr);
|
||||
} while (value & PGSTE_PCL_BIT);
|
||||
value |= PGSTE_PCL_BIT;
|
||||
#endif
|
||||
return __pgste(value);
|
||||
}
|
||||
|
||||
static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste)
|
||||
{
|
||||
#ifdef CONFIG_PGSTE
|
||||
barrier();
|
||||
WRITE_ONCE(*(unsigned long *)(ptep + PTRS_PER_PTE), pgste_val(pgste) & ~PGSTE_PCL_BIT);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* _S390_PAGE_H */
|
||||
|
||||
@@ -36,7 +36,6 @@ static inline bool __tlb_remove_folio_pages(struct mmu_gather *tlb,
|
||||
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm-generic/tlb.h>
|
||||
#include <asm/gmap.h>
|
||||
|
||||
/*
|
||||
* Release the page cache reference for a pte removed by
|
||||
@@ -85,8 +84,6 @@ static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
|
||||
tlb->mm->context.flush_mm = 1;
|
||||
tlb->freed_tables = 1;
|
||||
tlb->cleared_pmds = 1;
|
||||
if (mm_has_pgste(tlb->mm))
|
||||
gmap_unlink(tlb->mm, (unsigned long *)pte, address);
|
||||
tlb_remove_ptdesc(tlb, virt_to_ptdesc(pte));
|
||||
}
|
||||
|
||||
|
||||
@@ -471,65 +471,15 @@ do { \
|
||||
#define arch_get_kernel_nofault __mvc_kernel_nofault
|
||||
#define arch_put_kernel_nofault __mvc_kernel_nofault
|
||||
|
||||
void __cmpxchg_user_key_called_with_bad_pointer(void);
|
||||
|
||||
int __cmpxchg_user_key1(unsigned long address, unsigned char *uval,
|
||||
unsigned char old, unsigned char new, unsigned long key);
|
||||
int __cmpxchg_user_key2(unsigned long address, unsigned short *uval,
|
||||
unsigned short old, unsigned short new, unsigned long key);
|
||||
int __cmpxchg_user_key4(unsigned long address, unsigned int *uval,
|
||||
unsigned int old, unsigned int new, unsigned long key);
|
||||
int __cmpxchg_user_key8(unsigned long address, unsigned long *uval,
|
||||
unsigned long old, unsigned long new, unsigned long key);
|
||||
int __cmpxchg_user_key16(unsigned long address, __uint128_t *uval,
|
||||
__uint128_t old, __uint128_t new, unsigned long key);
|
||||
|
||||
static __always_inline int _cmpxchg_user_key(unsigned long address, void *uval,
|
||||
__uint128_t old, __uint128_t new,
|
||||
unsigned long key, int size)
|
||||
{
|
||||
switch (size) {
|
||||
case 1: return __cmpxchg_user_key1(address, uval, old, new, key);
|
||||
case 2: return __cmpxchg_user_key2(address, uval, old, new, key);
|
||||
case 4: return __cmpxchg_user_key4(address, uval, old, new, key);
|
||||
case 8: return __cmpxchg_user_key8(address, uval, old, new, key);
|
||||
case 16: return __cmpxchg_user_key16(address, uval, old, new, key);
|
||||
default: __cmpxchg_user_key_called_with_bad_pointer();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cmpxchg_user_key() - cmpxchg with user space target, honoring storage keys
|
||||
* @ptr: User space address of value to compare to @old and exchange with
|
||||
* @new. Must be aligned to sizeof(*@ptr).
|
||||
* @uval: Address where the old value of *@ptr is written to.
|
||||
* @old: Old value. Compared to the content pointed to by @ptr in order to
|
||||
* determine if the exchange occurs. The old value read from *@ptr is
|
||||
* written to *@uval.
|
||||
* @new: New value to place at *@ptr.
|
||||
* @key: Access key to use for checking storage key protection.
|
||||
*
|
||||
* Perform a cmpxchg on a user space target, honoring storage key protection.
|
||||
* @key alone determines how key checking is performed, neither
|
||||
* storage-protection-override nor fetch-protection-override apply.
|
||||
* The caller must compare *@uval and @old to determine if values have been
|
||||
* exchanged. In case of an exception *@uval is set to zero.
|
||||
*
|
||||
* Return: 0: cmpxchg executed
|
||||
* -EFAULT: an exception happened when trying to access *@ptr
|
||||
* -EAGAIN: maxed out number of retries (byte and short only)
|
||||
*/
|
||||
#define cmpxchg_user_key(ptr, uval, old, new, key) \
|
||||
({ \
|
||||
__typeof__(ptr) __ptr = (ptr); \
|
||||
__typeof__(uval) __uval = (uval); \
|
||||
\
|
||||
BUILD_BUG_ON(sizeof(*(__ptr)) != sizeof(*(__uval))); \
|
||||
might_fault(); \
|
||||
__chk_user_ptr(__ptr); \
|
||||
_cmpxchg_user_key((unsigned long)(__ptr), (void *)(__uval), \
|
||||
(old), (new), (key), sizeof(*(__ptr))); \
|
||||
})
|
||||
int __cmpxchg_key1(void *address, unsigned char *uval, unsigned char old,
|
||||
unsigned char new, unsigned long key);
|
||||
int __cmpxchg_key2(void *address, unsigned short *uval, unsigned short old,
|
||||
unsigned short new, unsigned long key);
|
||||
int __cmpxchg_key4(void *address, unsigned int *uval, unsigned int old,
|
||||
unsigned int new, unsigned long key);
|
||||
int __cmpxchg_key8(void *address, unsigned long *uval, unsigned long old,
|
||||
unsigned long new, unsigned long key);
|
||||
int __cmpxchg_key16(void *address, __uint128_t *uval, __uint128_t old,
|
||||
__uint128_t new, unsigned long key);
|
||||
|
||||
#endif /* __S390_UACCESS_H */
|
||||
|
||||
@@ -631,7 +631,8 @@ int uv_pin_shared(unsigned long paddr);
|
||||
int uv_destroy_folio(struct folio *folio);
|
||||
int uv_destroy_pte(pte_t pte);
|
||||
int uv_convert_from_secure_pte(pte_t pte);
|
||||
int make_hva_secure(struct mm_struct *mm, unsigned long hva, struct uv_cb_header *uvcb);
|
||||
int s390_wiggle_split_folio(struct mm_struct *mm, struct folio *folio);
|
||||
int __make_folio_secure(struct folio *folio, struct uv_cb_header *uvcb);
|
||||
int uv_convert_from_secure(unsigned long paddr);
|
||||
int uv_convert_from_secure_folio(struct folio *folio);
|
||||
|
||||
|
||||
+23
-119
@@ -134,14 +134,15 @@ static int uv_destroy(unsigned long paddr)
|
||||
*/
|
||||
int uv_destroy_folio(struct folio *folio)
|
||||
{
|
||||
unsigned long i;
|
||||
int rc;
|
||||
|
||||
/* Large folios cannot be secure */
|
||||
if (unlikely(folio_test_large(folio)))
|
||||
return 0;
|
||||
|
||||
folio_get(folio);
|
||||
rc = uv_destroy(folio_to_phys(folio));
|
||||
for (i = 0; i < (1 << folio_order(folio)); i++) {
|
||||
rc = uv_destroy(folio_to_phys(folio) + i * PAGE_SIZE);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
if (!rc)
|
||||
clear_bit(PG_arch_1, &folio->flags.f);
|
||||
folio_put(folio);
|
||||
@@ -183,14 +184,15 @@ EXPORT_SYMBOL_GPL(uv_convert_from_secure);
|
||||
*/
|
||||
int uv_convert_from_secure_folio(struct folio *folio)
|
||||
{
|
||||
unsigned long i;
|
||||
int rc;
|
||||
|
||||
/* Large folios cannot be secure */
|
||||
if (unlikely(folio_test_large(folio)))
|
||||
return 0;
|
||||
|
||||
folio_get(folio);
|
||||
rc = uv_convert_from_secure(folio_to_phys(folio));
|
||||
for (i = 0; i < (1 << folio_order(folio)); i++) {
|
||||
rc = uv_convert_from_secure(folio_to_phys(folio) + i * PAGE_SIZE);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
if (!rc)
|
||||
clear_bit(PG_arch_1, &folio->flags.f);
|
||||
folio_put(folio);
|
||||
@@ -207,39 +209,6 @@ int uv_convert_from_secure_pte(pte_t pte)
|
||||
return uv_convert_from_secure_folio(pfn_folio(pte_pfn(pte)));
|
||||
}
|
||||
|
||||
/**
|
||||
* should_export_before_import - Determine whether an export is needed
|
||||
* before an import-like operation
|
||||
* @uvcb: the Ultravisor control block of the UVC to be performed
|
||||
* @mm: the mm of the process
|
||||
*
|
||||
* Returns whether an export is needed before every import-like operation.
|
||||
* This is needed for shared pages, which don't trigger a secure storage
|
||||
* exception when accessed from a different guest.
|
||||
*
|
||||
* Although considered as one, the Unpin Page UVC is not an actual import,
|
||||
* so it is not affected.
|
||||
*
|
||||
* No export is needed also when there is only one protected VM, because the
|
||||
* page cannot belong to the wrong VM in that case (there is no "other VM"
|
||||
* it can belong to).
|
||||
*
|
||||
* Return: true if an export is needed before every import, otherwise false.
|
||||
*/
|
||||
static bool should_export_before_import(struct uv_cb_header *uvcb, struct mm_struct *mm)
|
||||
{
|
||||
/*
|
||||
* The misc feature indicates, among other things, that importing a
|
||||
* shared page from a different protected VM will automatically also
|
||||
* transfer its ownership.
|
||||
*/
|
||||
if (uv_has_feature(BIT_UV_FEAT_MISC))
|
||||
return false;
|
||||
if (uvcb->cmd == UVC_CMD_UNPIN_PAGE_SHARED)
|
||||
return false;
|
||||
return atomic_read(&mm->context.protected_count) > 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the expected ref_count for a folio that would otherwise have no
|
||||
* further pins. This was cribbed from similar functions in other places in
|
||||
@@ -279,7 +248,7 @@ static int expected_folio_refs(struct folio *folio)
|
||||
* (it's the same logic as split_folio()), and the folio must be
|
||||
* locked.
|
||||
*/
|
||||
static int __make_folio_secure(struct folio *folio, struct uv_cb_header *uvcb)
|
||||
int __make_folio_secure(struct folio *folio, struct uv_cb_header *uvcb)
|
||||
{
|
||||
int expected, cc = 0;
|
||||
|
||||
@@ -309,20 +278,7 @@ static int __make_folio_secure(struct folio *folio, struct uv_cb_header *uvcb)
|
||||
return -EAGAIN;
|
||||
return uvcb->rc == 0x10a ? -ENXIO : -EINVAL;
|
||||
}
|
||||
|
||||
static int make_folio_secure(struct mm_struct *mm, struct folio *folio, struct uv_cb_header *uvcb)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!folio_trylock(folio))
|
||||
return -EAGAIN;
|
||||
if (should_export_before_import(uvcb, mm))
|
||||
uv_convert_from_secure(folio_to_phys(folio));
|
||||
rc = __make_folio_secure(folio, uvcb);
|
||||
folio_unlock(folio);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(__make_folio_secure);
|
||||
|
||||
/**
|
||||
* s390_wiggle_split_folio() - try to drain extra references to a folio and
|
||||
@@ -337,7 +293,7 @@ static int make_folio_secure(struct mm_struct *mm, struct folio *folio, struct u
|
||||
* but another attempt can be made;
|
||||
* -EINVAL in case of other folio splitting errors. See split_folio().
|
||||
*/
|
||||
static int s390_wiggle_split_folio(struct mm_struct *mm, struct folio *folio)
|
||||
int s390_wiggle_split_folio(struct mm_struct *mm, struct folio *folio)
|
||||
{
|
||||
int rc, tried_splits;
|
||||
|
||||
@@ -409,56 +365,7 @@ static int s390_wiggle_split_folio(struct mm_struct *mm, struct folio *folio)
|
||||
}
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
int make_hva_secure(struct mm_struct *mm, unsigned long hva, struct uv_cb_header *uvcb)
|
||||
{
|
||||
struct vm_area_struct *vma;
|
||||
struct folio_walk fw;
|
||||
struct folio *folio;
|
||||
int rc;
|
||||
|
||||
mmap_read_lock(mm);
|
||||
vma = vma_lookup(mm, hva);
|
||||
if (!vma) {
|
||||
mmap_read_unlock(mm);
|
||||
return -EFAULT;
|
||||
}
|
||||
folio = folio_walk_start(&fw, vma, hva, 0);
|
||||
if (!folio) {
|
||||
mmap_read_unlock(mm);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
folio_get(folio);
|
||||
/*
|
||||
* Secure pages cannot be huge and userspace should not combine both.
|
||||
* In case userspace does it anyway this will result in an -EFAULT for
|
||||
* the unpack. The guest is thus never reaching secure mode.
|
||||
* If userspace plays dirty tricks and decides to map huge pages at a
|
||||
* later point in time, it will receive a segmentation fault or
|
||||
* KVM_RUN will return -EFAULT.
|
||||
*/
|
||||
if (folio_test_hugetlb(folio))
|
||||
rc = -EFAULT;
|
||||
else if (folio_test_large(folio))
|
||||
rc = -E2BIG;
|
||||
else if (!pte_write(fw.pte) || (pte_val(fw.pte) & _PAGE_INVALID))
|
||||
rc = -ENXIO;
|
||||
else
|
||||
rc = make_folio_secure(mm, folio, uvcb);
|
||||
folio_walk_end(&fw, vma);
|
||||
mmap_read_unlock(mm);
|
||||
|
||||
if (rc == -E2BIG || rc == -EBUSY) {
|
||||
rc = s390_wiggle_split_folio(mm, folio);
|
||||
if (!rc)
|
||||
rc = -EAGAIN;
|
||||
}
|
||||
folio_put(folio);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(make_hva_secure);
|
||||
EXPORT_SYMBOL_GPL(s390_wiggle_split_folio);
|
||||
|
||||
/*
|
||||
* To be called with the folio locked or with an extra reference! This will
|
||||
@@ -470,21 +377,18 @@ int arch_make_folio_accessible(struct folio *folio)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
/* Large folios cannot be secure */
|
||||
if (unlikely(folio_test_large(folio)))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* PG_arch_1 is used in 2 places:
|
||||
* 1. for storage keys of hugetlb folios and KVM
|
||||
* 2. As an indication that this small folio might be secure. This can
|
||||
* overindicate, e.g. we set the bit before calling
|
||||
* convert_to_secure.
|
||||
* As secure pages are never large folios, both variants can co-exists.
|
||||
* PG_arch_1 is used as an indication that this small folio might be
|
||||
* secure. This can overindicate, e.g. we set the bit before calling
|
||||
* convert_to_secure.
|
||||
*/
|
||||
if (!test_bit(PG_arch_1, &folio->flags.f))
|
||||
return 0;
|
||||
|
||||
/* Large folios cannot be secure. */
|
||||
if (WARN_ON_ONCE(folio_test_large(folio)))
|
||||
return -EFAULT;
|
||||
|
||||
rc = uv_pin_shared(folio_to_phys(folio));
|
||||
if (!rc) {
|
||||
clear_bit(PG_arch_1, &folio->flags.f);
|
||||
|
||||
@@ -30,6 +30,8 @@ config KVM
|
||||
select KVM_VFIO
|
||||
select MMU_NOTIFIER
|
||||
select VIRT_XFER_TO_GUEST_WORK
|
||||
select KVM_GENERIC_MMU_NOTIFIER
|
||||
select KVM_MMU_LOCKLESS_AGING
|
||||
help
|
||||
Support hosting paravirtualized guest machines using the SIE
|
||||
virtualization capability on the mainframe. This should work
|
||||
|
||||
@@ -8,7 +8,8 @@ include $(srctree)/virt/kvm/Makefile.kvm
|
||||
ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
|
||||
|
||||
kvm-y += kvm-s390.o intercept.o interrupt.o priv.o sigp.o
|
||||
kvm-y += diag.o gaccess.o guestdbg.o vsie.o pv.o gmap-vsie.o
|
||||
kvm-y += diag.o gaccess.o guestdbg.o vsie.o pv.o
|
||||
kvm-y += dat.o gmap.o faultin.o
|
||||
|
||||
kvm-$(CONFIG_VFIO_PCI_ZDEV_KVM) += pci.o
|
||||
obj-$(CONFIG_KVM) += kvm.o
|
||||
|
||||
+1391
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user