KVM: s390: Changes for 5.18 part1
- add Claudio as Maintainer - first step to do proper storage key checking - testcase for missing memop check -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+SKTgaM0CPnbq/vKEXu8gLWmHHwFAmIUnCkACgkQEXu8gLWm HHxagA//ZY02kmPB8Q6cd7xju+9wxAtdvBAorSEFO8nyW0tPwI8tPGX2AbrGywwh ZDSXRRDGK5PgYWzWAlOO59YbZ9b+HqDJV3yDfDVwGX8JauRVo70DFYxLudD4IKIZ 9bbxm9jjc1S0+nxpBaQZzuTGGKVxl88/GAH4s5Ce5liYPjYn1XWOuHh3Udl1fx5g 4DiKGcpTSYKwcU9aGuv8l680T8CEpzhluLHuz/IG1p5do+NCmyxXAt9IUWnR7LhS 2ram6IB2kHo3ygbiB354Qd6A/uy8I/Ow3HKoyN9QfppOYEowyLM0q2BT0tMwba7j bkd2fYjegUw4e5eWCozZ5T6OWNt4YPZO7Y8R/AW3WeE//AeTJWKeqX3tckU5gukh Th0SQyujLzTlJUFCxEsKbH2ljmX5qiIl/67C6nSdycY5du1cCMezattV0FC/iX3q 0nnDTk5jcMd2G1gHEmrBvBXlhN29e0VNiRgZ5KG6F53DRkJzNdP18Q5DMHi5icJ+ rkYFfQ/ROc+TSsL2xaczmvHMesN78ts+V/4tIrObG7CCe6taSCPwrj4u1tIPvQ++ jfxSw0v5rqXUwnCUQ+F9vt2H4nryijCooNt9rsWY63JPblmLs/8rfF72wJd0edY4 6+HUCAyYksoW7MKl3DyOJnG/AgHy5Kq114oloPH8NykpcL3RiHE= =qGQI -----END PGP SIGNATURE----- Merge tag 'kvm-s390-next-5.18-1' of git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into HEAD KVM: s390: Changes for 5.18 part1 - add Claudio as Maintainer - first step to do proper storage key checking - testcase for missing memop check
This commit is contained in:
commit
0828824158
@ -3683,15 +3683,17 @@ The fields in each entry are defined as follows:
|
||||
4.89 KVM_S390_MEM_OP
|
||||
--------------------
|
||||
|
||||
:Capability: KVM_CAP_S390_MEM_OP
|
||||
:Capability: KVM_CAP_S390_MEM_OP, KVM_CAP_S390_PROTECTED, KVM_CAP_S390_MEM_OP_EXTENSION
|
||||
:Architectures: s390
|
||||
:Type: vcpu ioctl
|
||||
:Type: vm ioctl, vcpu ioctl
|
||||
:Parameters: struct kvm_s390_mem_op (in)
|
||||
:Returns: = 0 on success,
|
||||
< 0 on generic error (e.g. -EFAULT or -ENOMEM),
|
||||
> 0 if an exception occurred while walking the page tables
|
||||
|
||||
Read or write data from/to the logical (virtual) memory of a VCPU.
|
||||
Read or write data from/to the VM's memory.
|
||||
The KVM_CAP_S390_MEM_OP_EXTENSION capability specifies what functionality is
|
||||
supported.
|
||||
|
||||
Parameters are specified via the following structure::
|
||||
|
||||
@ -3701,33 +3703,99 @@ Parameters are specified via the following structure::
|
||||
__u32 size; /* amount of bytes */
|
||||
__u32 op; /* type of operation */
|
||||
__u64 buf; /* buffer in userspace */
|
||||
__u8 ar; /* the access register number */
|
||||
__u8 reserved[31]; /* should be set to 0 */
|
||||
union {
|
||||
struct {
|
||||
__u8 ar; /* the access register number */
|
||||
__u8 key; /* access key, ignored if flag unset */
|
||||
};
|
||||
__u32 sida_offset; /* offset into the sida */
|
||||
__u8 reserved[32]; /* ignored */
|
||||
};
|
||||
};
|
||||
|
||||
The type of operation is specified in the "op" field. It is either
|
||||
KVM_S390_MEMOP_LOGICAL_READ for reading from logical memory space or
|
||||
KVM_S390_MEMOP_LOGICAL_WRITE for writing to logical memory space. The
|
||||
KVM_S390_MEMOP_F_CHECK_ONLY flag can be set in the "flags" field to check
|
||||
whether the corresponding memory access would create an access exception
|
||||
(without touching the data in the memory at the destination). In case an
|
||||
access exception occurred while walking the MMU tables of the guest, the
|
||||
ioctl returns a positive error number to indicate the type of exception.
|
||||
This exception is also raised directly at the corresponding VCPU if the
|
||||
flag KVM_S390_MEMOP_F_INJECT_EXCEPTION is set in the "flags" field.
|
||||
|
||||
The start address of the memory region has to be specified in the "gaddr"
|
||||
field, and the length of the region in the "size" field (which must not
|
||||
be 0). The maximum value for "size" can be obtained by checking the
|
||||
KVM_CAP_S390_MEM_OP capability. "buf" is the buffer supplied by the
|
||||
userspace application where the read data should be written to for
|
||||
KVM_S390_MEMOP_LOGICAL_READ, or where the data that should be written is
|
||||
stored for a KVM_S390_MEMOP_LOGICAL_WRITE. When KVM_S390_MEMOP_F_CHECK_ONLY
|
||||
is specified, "buf" is unused and can be NULL. "ar" designates the access
|
||||
register number to be used; the valid range is 0..15.
|
||||
a read access, or where the data that should be written is stored for
|
||||
a write access. The "reserved" field is meant for future extensions.
|
||||
Reserved and unused values are ignored. Future extension that add members must
|
||||
introduce new flags.
|
||||
|
||||
The "reserved" field is meant for future extensions. It is not used by
|
||||
KVM with the currently defined set of flags.
|
||||
The type of operation is specified in the "op" field. Flags modifying
|
||||
their behavior can be set in the "flags" field. Undefined flag bits must
|
||||
be set to 0.
|
||||
|
||||
Possible operations are:
|
||||
* ``KVM_S390_MEMOP_LOGICAL_READ``
|
||||
* ``KVM_S390_MEMOP_LOGICAL_WRITE``
|
||||
* ``KVM_S390_MEMOP_ABSOLUTE_READ``
|
||||
* ``KVM_S390_MEMOP_ABSOLUTE_WRITE``
|
||||
* ``KVM_S390_MEMOP_SIDA_READ``
|
||||
* ``KVM_S390_MEMOP_SIDA_WRITE``
|
||||
|
||||
Logical read/write:
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Access logical memory, i.e. translate the given guest address to an absolute
|
||||
address given the state of the VCPU and use the absolute address as target of
|
||||
the access. "ar" designates the access register number to be used; the valid
|
||||
range is 0..15.
|
||||
Logical accesses are permitted for the VCPU ioctl only.
|
||||
Logical accesses are permitted for non-protected guests only.
|
||||
|
||||
Supported flags:
|
||||
* ``KVM_S390_MEMOP_F_CHECK_ONLY``
|
||||
* ``KVM_S390_MEMOP_F_INJECT_EXCEPTION``
|
||||
* ``KVM_S390_MEMOP_F_SKEY_PROTECTION``
|
||||
|
||||
The KVM_S390_MEMOP_F_CHECK_ONLY flag can be set to check whether the
|
||||
corresponding memory access would cause an access exception; however,
|
||||
no actual access to the data in memory at the destination is performed.
|
||||
In this case, "buf" is unused and can be NULL.
|
||||
|
||||
In case an access exception occurred during the access (or would occur
|
||||
in case of KVM_S390_MEMOP_F_CHECK_ONLY), the ioctl returns a positive
|
||||
error number indicating the type of exception. This exception is also
|
||||
raised directly at the corresponding VCPU if the flag
|
||||
KVM_S390_MEMOP_F_INJECT_EXCEPTION is set.
|
||||
|
||||
If the KVM_S390_MEMOP_F_SKEY_PROTECTION flag is set, storage key
|
||||
protection is also in effect and may cause exceptions if accesses are
|
||||
prohibited given the access key designated by "key"; the valid range is 0..15.
|
||||
KVM_S390_MEMOP_F_SKEY_PROTECTION is available if KVM_CAP_S390_MEM_OP_EXTENSION
|
||||
is > 0.
|
||||
|
||||
Absolute read/write:
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Access absolute memory. This operation is intended to be used with the
|
||||
KVM_S390_MEMOP_F_SKEY_PROTECTION flag, to allow accessing memory and performing
|
||||
the checks required for storage key protection as one operation (as opposed to
|
||||
user space getting the storage keys, performing the checks, and accessing
|
||||
memory thereafter, which could lead to a delay between check and access).
|
||||
Absolute accesses are permitted for the VM ioctl if KVM_CAP_S390_MEM_OP_EXTENSION
|
||||
is > 0.
|
||||
Currently absolute accesses are not permitted for VCPU ioctls.
|
||||
Absolute accesses are permitted for non-protected guests only.
|
||||
|
||||
Supported flags:
|
||||
* ``KVM_S390_MEMOP_F_CHECK_ONLY``
|
||||
* ``KVM_S390_MEMOP_F_SKEY_PROTECTION``
|
||||
|
||||
The semantics of the flags are as for logical accesses.
|
||||
|
||||
SIDA read/write:
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Access the secure instruction data area which contains memory operands necessary
|
||||
for instruction emulation for protected guests.
|
||||
SIDA accesses are available if the KVM_CAP_S390_PROTECTED capability is available.
|
||||
SIDA accesses are permitted for the VCPU ioctl only.
|
||||
SIDA accesses are permitted for protected guests only.
|
||||
|
||||
No flags are supported.
|
||||
|
||||
4.90 KVM_S390_GET_SKEYS
|
||||
-----------------------
|
||||
|
@ -10547,8 +10547,8 @@ F: arch/riscv/kvm/
|
||||
KERNEL VIRTUAL MACHINE for s390 (KVM/s390)
|
||||
M: Christian Borntraeger <borntraeger@linux.ibm.com>
|
||||
M: Janosch Frank <frankja@linux.ibm.com>
|
||||
M: Claudio Imbrenda <imbrenda@linux.ibm.com>
|
||||
R: David Hildenbrand <david@redhat.com>
|
||||
R: Claudio Imbrenda <imbrenda@linux.ibm.com>
|
||||
L: kvm@vger.kernel.org
|
||||
S: Supported
|
||||
W: http://www.ibm.com/developerworks/linux/linux390/
|
||||
@ -13571,7 +13571,7 @@ F: tools/testing/selftests/nci/
|
||||
|
||||
NFS, SUNRPC, AND LOCKD CLIENTS
|
||||
M: Trond Myklebust <trond.myklebust@hammerspace.com>
|
||||
M: Anna Schumaker <anna.schumaker@netapp.com>
|
||||
M: Anna Schumaker <anna@kernel.org>
|
||||
L: linux-nfs@vger.kernel.org
|
||||
S: Maintained
|
||||
W: http://client.linux-nfs.org
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
#define CR0_CLOCK_COMPARATOR_SIGN BIT(63 - 10)
|
||||
#define CR0_LOW_ADDRESS_PROTECTION BIT(63 - 35)
|
||||
#define CR0_FETCH_PROTECTION_OVERRIDE BIT(63 - 38)
|
||||
#define CR0_STORAGE_PROTECTION_OVERRIDE BIT(63 - 39)
|
||||
#define CR0_EMERGENCY_SIGNAL_SUBMASK BIT(63 - 49)
|
||||
#define CR0_EXTERNAL_CALL_SUBMASK BIT(63 - 50)
|
||||
#define CR0_CLOCK_COMPARATOR_SUBMASK BIT(63 - 52)
|
||||
|
@ -20,6 +20,8 @@
|
||||
#define PAGE_SIZE _PAGE_SIZE
|
||||
#define PAGE_MASK _PAGE_MASK
|
||||
#define PAGE_DEFAULT_ACC 0
|
||||
/* storage-protection override */
|
||||
#define PAGE_SPO_ACC 9
|
||||
#define PAGE_DEFAULT_KEY (PAGE_DEFAULT_ACC << 4)
|
||||
|
||||
#define HPAGE_SHIFT 20
|
||||
|
@ -44,6 +44,28 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n);
|
||||
#define INLINE_COPY_TO_USER
|
||||
#endif
|
||||
|
||||
unsigned long __must_check
|
||||
_copy_from_user_key(void *to, const void __user *from, unsigned long n, unsigned long key);
|
||||
|
||||
static __always_inline unsigned long __must_check
|
||||
copy_from_user_key(void *to, const void __user *from, unsigned long n, unsigned long key)
|
||||
{
|
||||
if (likely(check_copy_size(to, n, false)))
|
||||
n = _copy_from_user_key(to, from, n, key);
|
||||
return n;
|
||||
}
|
||||
|
||||
unsigned long __must_check
|
||||
_copy_to_user_key(void __user *to, const void *from, unsigned long n, unsigned long key);
|
||||
|
||||
static __always_inline unsigned long __must_check
|
||||
copy_to_user_key(void __user *to, const void *from, unsigned long n, unsigned long key)
|
||||
{
|
||||
if (likely(check_copy_size(from, n, true)))
|
||||
n = _copy_to_user_key(to, from, n, key);
|
||||
return n;
|
||||
}
|
||||
|
||||
int __put_user_bad(void) __attribute__((noreturn));
|
||||
int __get_user_bad(void) __attribute__((noreturn));
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <linux/mm_types.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/pgtable.h>
|
||||
#include <linux/bitfield.h>
|
||||
|
||||
#include <asm/gmap.h>
|
||||
#include "kvm-s390.h"
|
||||
@ -794,6 +795,108 @@ static int low_address_protection_enabled(struct kvm_vcpu *vcpu,
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int vm_check_access_key(struct kvm *kvm, u8 access_key,
|
||||
enum gacc_mode mode, gpa_t gpa)
|
||||
{
|
||||
u8 storage_key, access_control;
|
||||
bool fetch_protected;
|
||||
unsigned long hva;
|
||||
int r;
|
||||
|
||||
if (access_key == 0)
|
||||
return 0;
|
||||
|
||||
hva = gfn_to_hva(kvm, gpa_to_gfn(gpa));
|
||||
if (kvm_is_error_hva(hva))
|
||||
return PGM_ADDRESSING;
|
||||
|
||||
mmap_read_lock(current->mm);
|
||||
r = get_guest_storage_key(current->mm, hva, &storage_key);
|
||||
mmap_read_unlock(current->mm);
|
||||
if (r)
|
||||
return r;
|
||||
access_control = FIELD_GET(_PAGE_ACC_BITS, storage_key);
|
||||
if (access_control == access_key)
|
||||
return 0;
|
||||
fetch_protected = storage_key & _PAGE_FP_BIT;
|
||||
if ((mode == GACC_FETCH || mode == GACC_IFETCH) && !fetch_protected)
|
||||
return 0;
|
||||
return PGM_PROTECTION;
|
||||
}
|
||||
|
||||
static bool fetch_prot_override_applicable(struct kvm_vcpu *vcpu, enum gacc_mode mode,
|
||||
union asce asce)
|
||||
{
|
||||
psw_t *psw = &vcpu->arch.sie_block->gpsw;
|
||||
unsigned long override;
|
||||
|
||||
if (mode == GACC_FETCH || mode == GACC_IFETCH) {
|
||||
/* check if fetch protection override enabled */
|
||||
override = vcpu->arch.sie_block->gcr[0];
|
||||
override &= CR0_FETCH_PROTECTION_OVERRIDE;
|
||||
/* not applicable if subject to DAT && private space */
|
||||
override = override && !(psw_bits(*psw).dat && asce.p);
|
||||
return override;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool fetch_prot_override_applies(unsigned long ga, unsigned int len)
|
||||
{
|
||||
return ga < 2048 && ga + len <= 2048;
|
||||
}
|
||||
|
||||
static bool storage_prot_override_applicable(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
/* check if storage protection override enabled */
|
||||
return vcpu->arch.sie_block->gcr[0] & CR0_STORAGE_PROTECTION_OVERRIDE;
|
||||
}
|
||||
|
||||
static bool storage_prot_override_applies(u8 access_control)
|
||||
{
|
||||
/* matches special storage protection override key (9) -> allow */
|
||||
return access_control == PAGE_SPO_ACC;
|
||||
}
|
||||
|
||||
static int vcpu_check_access_key(struct kvm_vcpu *vcpu, u8 access_key,
|
||||
enum gacc_mode mode, union asce asce, gpa_t gpa,
|
||||
unsigned long ga, unsigned int len)
|
||||
{
|
||||
u8 storage_key, access_control;
|
||||
unsigned long hva;
|
||||
int r;
|
||||
|
||||
/* access key 0 matches any storage key -> allow */
|
||||
if (access_key == 0)
|
||||
return 0;
|
||||
/*
|
||||
* caller needs to ensure that gfn is accessible, so we can
|
||||
* assume that this cannot fail
|
||||
*/
|
||||
hva = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gpa));
|
||||
mmap_read_lock(current->mm);
|
||||
r = get_guest_storage_key(current->mm, hva, &storage_key);
|
||||
mmap_read_unlock(current->mm);
|
||||
if (r)
|
||||
return r;
|
||||
access_control = FIELD_GET(_PAGE_ACC_BITS, storage_key);
|
||||
/* access key matches storage key -> allow */
|
||||
if (access_control == access_key)
|
||||
return 0;
|
||||
if (mode == GACC_FETCH || mode == GACC_IFETCH) {
|
||||
/* it is a fetch and fetch protection is off -> allow */
|
||||
if (!(storage_key & _PAGE_FP_BIT))
|
||||
return 0;
|
||||
if (fetch_prot_override_applicable(vcpu, mode, asce) &&
|
||||
fetch_prot_override_applies(ga, len))
|
||||
return 0;
|
||||
}
|
||||
if (storage_prot_override_applicable(vcpu) &&
|
||||
storage_prot_override_applies(access_control))
|
||||
return 0;
|
||||
return PGM_PROTECTION;
|
||||
}
|
||||
|
||||
/**
|
||||
* guest_range_to_gpas() - Calculate guest physical addresses of page fragments
|
||||
* covering a logical range
|
||||
@ -804,6 +907,7 @@ static int low_address_protection_enabled(struct kvm_vcpu *vcpu,
|
||||
* @len: length of range in bytes
|
||||
* @asce: address-space-control element to use for translation
|
||||
* @mode: access mode
|
||||
* @access_key: access key to mach the range's storage keys against
|
||||
*
|
||||
* Translate a logical range to a series of guest absolute addresses,
|
||||
* such that the concatenation of page fragments starting at each gpa make up
|
||||
@ -830,7 +934,8 @@ static int low_address_protection_enabled(struct kvm_vcpu *vcpu,
|
||||
*/
|
||||
static int guest_range_to_gpas(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
|
||||
unsigned long *gpas, unsigned long len,
|
||||
const union asce asce, enum gacc_mode mode)
|
||||
const union asce asce, enum gacc_mode mode,
|
||||
u8 access_key)
|
||||
{
|
||||
psw_t *psw = &vcpu->arch.sie_block->gpsw;
|
||||
unsigned int offset = offset_in_page(ga);
|
||||
@ -857,6 +962,10 @@ static int guest_range_to_gpas(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
|
||||
}
|
||||
if (rc)
|
||||
return trans_exc(vcpu, rc, ga, ar, mode, prot);
|
||||
rc = vcpu_check_access_key(vcpu, access_key, mode, asce, gpa, ga,
|
||||
fragment_len);
|
||||
if (rc)
|
||||
return trans_exc(vcpu, rc, ga, ar, mode, PROT_TYPE_KEYC);
|
||||
if (gpas)
|
||||
*gpas++ = gpa;
|
||||
offset = 0;
|
||||
@ -880,16 +989,74 @@ static int access_guest_page(struct kvm *kvm, enum gacc_mode mode, gpa_t gpa,
|
||||
return rc;
|
||||
}
|
||||
|
||||
int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data,
|
||||
unsigned long len, enum gacc_mode mode)
|
||||
static int
|
||||
access_guest_page_with_key(struct kvm *kvm, enum gacc_mode mode, gpa_t gpa,
|
||||
void *data, unsigned int len, u8 access_key)
|
||||
{
|
||||
struct kvm_memory_slot *slot;
|
||||
bool writable;
|
||||
gfn_t gfn;
|
||||
hva_t hva;
|
||||
int rc;
|
||||
|
||||
gfn = gpa >> PAGE_SHIFT;
|
||||
slot = gfn_to_memslot(kvm, gfn);
|
||||
hva = gfn_to_hva_memslot_prot(slot, gfn, &writable);
|
||||
|
||||
if (kvm_is_error_hva(hva))
|
||||
return PGM_ADDRESSING;
|
||||
/*
|
||||
* Check if it's a ro memslot, even tho that can't occur (they're unsupported).
|
||||
* Don't try to actually handle that case.
|
||||
*/
|
||||
if (!writable && mode == GACC_STORE)
|
||||
return -EOPNOTSUPP;
|
||||
hva += offset_in_page(gpa);
|
||||
if (mode == GACC_STORE)
|
||||
rc = copy_to_user_key((void __user *)hva, data, len, access_key);
|
||||
else
|
||||
rc = copy_from_user_key(data, (void __user *)hva, len, access_key);
|
||||
if (rc)
|
||||
return PGM_PROTECTION;
|
||||
if (mode == GACC_STORE)
|
||||
mark_page_dirty_in_slot(kvm, slot, gfn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int access_guest_abs_with_key(struct kvm *kvm, gpa_t gpa, void *data,
|
||||
unsigned long len, enum gacc_mode mode, u8 access_key)
|
||||
{
|
||||
int offset = offset_in_page(gpa);
|
||||
int fragment_len;
|
||||
int rc;
|
||||
|
||||
while (min(PAGE_SIZE - offset, len) > 0) {
|
||||
fragment_len = min(PAGE_SIZE - offset, len);
|
||||
rc = access_guest_page_with_key(kvm, mode, gpa, data, fragment_len, access_key);
|
||||
if (rc)
|
||||
return rc;
|
||||
offset = 0;
|
||||
len -= fragment_len;
|
||||
data += fragment_len;
|
||||
gpa += fragment_len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int access_guest_with_key(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
|
||||
void *data, unsigned long len, enum gacc_mode mode,
|
||||
u8 access_key)
|
||||
{
|
||||
psw_t *psw = &vcpu->arch.sie_block->gpsw;
|
||||
unsigned long nr_pages, idx;
|
||||
unsigned long gpa_array[2];
|
||||
unsigned int fragment_len;
|
||||
unsigned long *gpas;
|
||||
enum prot_type prot;
|
||||
int need_ipte_lock;
|
||||
union asce asce;
|
||||
bool try_storage_prot_override;
|
||||
bool try_fetch_prot_override;
|
||||
int rc;
|
||||
|
||||
if (!len)
|
||||
@ -904,16 +1071,47 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data,
|
||||
gpas = vmalloc(array_size(nr_pages, sizeof(unsigned long)));
|
||||
if (!gpas)
|
||||
return -ENOMEM;
|
||||
try_fetch_prot_override = fetch_prot_override_applicable(vcpu, mode, asce);
|
||||
try_storage_prot_override = storage_prot_override_applicable(vcpu);
|
||||
need_ipte_lock = psw_bits(*psw).dat && !asce.r;
|
||||
if (need_ipte_lock)
|
||||
ipte_lock(vcpu);
|
||||
rc = guest_range_to_gpas(vcpu, ga, ar, gpas, len, asce, mode);
|
||||
for (idx = 0; idx < nr_pages && !rc; idx++) {
|
||||
/*
|
||||
* Since we do the access further down ultimately via a move instruction
|
||||
* that does key checking and returns an error in case of a protection
|
||||
* violation, we don't need to do the check during address translation.
|
||||
* Skip it by passing access key 0, which matches any storage key,
|
||||
* obviating the need for any further checks. As a result the check is
|
||||
* handled entirely in hardware on access, we only need to take care to
|
||||
* forego key protection checking if fetch protection override applies or
|
||||
* retry with the special key 9 in case of storage protection override.
|
||||
*/
|
||||
rc = guest_range_to_gpas(vcpu, ga, ar, gpas, len, asce, mode, 0);
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
for (idx = 0; idx < nr_pages; idx++) {
|
||||
fragment_len = min(PAGE_SIZE - offset_in_page(gpas[idx]), len);
|
||||
rc = access_guest_page(vcpu->kvm, mode, gpas[idx], data, fragment_len);
|
||||
if (try_fetch_prot_override && fetch_prot_override_applies(ga, fragment_len)) {
|
||||
rc = access_guest_page(vcpu->kvm, mode, gpas[idx],
|
||||
data, fragment_len);
|
||||
} else {
|
||||
rc = access_guest_page_with_key(vcpu->kvm, mode, gpas[idx],
|
||||
data, fragment_len, access_key);
|
||||
}
|
||||
if (rc == PGM_PROTECTION && try_storage_prot_override)
|
||||
rc = access_guest_page_with_key(vcpu->kvm, mode, gpas[idx],
|
||||
data, fragment_len, PAGE_SPO_ACC);
|
||||
if (rc == PGM_PROTECTION)
|
||||
prot = PROT_TYPE_KEYC;
|
||||
if (rc)
|
||||
break;
|
||||
len -= fragment_len;
|
||||
data += fragment_len;
|
||||
ga = kvm_s390_logical_to_effective(vcpu, ga + fragment_len);
|
||||
}
|
||||
if (rc > 0)
|
||||
rc = trans_exc(vcpu, rc, ga, ar, mode, prot);
|
||||
out_unlock:
|
||||
if (need_ipte_lock)
|
||||
ipte_unlock(vcpu);
|
||||
if (nr_pages > ARRAY_SIZE(gpa_array))
|
||||
@ -940,12 +1138,13 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
|
||||
}
|
||||
|
||||
/**
|
||||
* guest_translate_address - translate guest logical into guest absolute address
|
||||
* guest_translate_address_with_key - translate guest logical into guest absolute address
|
||||
* @vcpu: virtual cpu
|
||||
* @gva: Guest virtual address
|
||||
* @ar: Access register
|
||||
* @gpa: Guest physical address
|
||||
* @mode: Translation access mode
|
||||
* @access_key: access key to mach the storage key with
|
||||
*
|
||||
* Parameter semantics are the same as the ones from guest_translate.
|
||||
* The memory contents at the guest address are not changed.
|
||||
@ -953,8 +1152,9 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
|
||||
* Note: The IPTE lock is not taken during this function, so the caller
|
||||
* has to take care of this.
|
||||
*/
|
||||
int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
|
||||
unsigned long *gpa, enum gacc_mode mode)
|
||||
int guest_translate_address_with_key(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
|
||||
unsigned long *gpa, enum gacc_mode mode,
|
||||
u8 access_key)
|
||||
{
|
||||
union asce asce;
|
||||
int rc;
|
||||
@ -963,7 +1163,8 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
|
||||
rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode);
|
||||
if (rc)
|
||||
return rc;
|
||||
return guest_range_to_gpas(vcpu, gva, ar, gpa, 1, asce, mode);
|
||||
return guest_range_to_gpas(vcpu, gva, ar, gpa, 1, asce, mode,
|
||||
access_key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -973,9 +1174,10 @@ int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
|
||||
* @ar: Access register
|
||||
* @length: Length of test range
|
||||
* @mode: Translation access mode
|
||||
* @access_key: access key to mach the storage keys with
|
||||
*/
|
||||
int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
|
||||
unsigned long length, enum gacc_mode mode)
|
||||
unsigned long length, enum gacc_mode mode, u8 access_key)
|
||||
{
|
||||
union asce asce;
|
||||
int rc = 0;
|
||||
@ -984,12 +1186,36 @@ int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
|
||||
if (rc)
|
||||
return rc;
|
||||
ipte_lock(vcpu);
|
||||
rc = guest_range_to_gpas(vcpu, gva, ar, NULL, length, asce, mode);
|
||||
rc = guest_range_to_gpas(vcpu, gva, ar, NULL, length, asce, mode,
|
||||
access_key);
|
||||
ipte_unlock(vcpu);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* check_gpa_range - test a range of guest physical addresses for accessibility
|
||||
* @kvm: virtual machine instance
|
||||
* @gpa: guest physical address
|
||||
* @length: length of test range
|
||||
* @mode: access mode to test, relevant for storage keys
|
||||
* @access_key: access key to mach the storage keys with
|
||||
*/
|
||||
int check_gpa_range(struct kvm *kvm, unsigned long gpa, unsigned long length,
|
||||
enum gacc_mode mode, u8 access_key)
|
||||
{
|
||||
unsigned int fragment_len;
|
||||
int rc = 0;
|
||||
|
||||
while (length && !rc) {
|
||||
fragment_len = min(PAGE_SIZE - offset_in_page(gpa), length);
|
||||
rc = vm_check_access_key(kvm, access_key, mode, gpa);
|
||||
length -= fragment_len;
|
||||
gpa += fragment_len;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_s390_check_low_addr_prot_real - check for low-address protection
|
||||
* @vcpu: virtual cpu
|
||||
|
@ -186,24 +186,34 @@ enum gacc_mode {
|
||||
GACC_IFETCH,
|
||||
};
|
||||
|
||||
int guest_translate_address(struct kvm_vcpu *vcpu, unsigned long gva,
|
||||
u8 ar, unsigned long *gpa, enum gacc_mode mode);
|
||||
int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
|
||||
unsigned long length, enum gacc_mode mode);
|
||||
int guest_translate_address_with_key(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
|
||||
unsigned long *gpa, enum gacc_mode mode,
|
||||
u8 access_key);
|
||||
|
||||
int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data,
|
||||
unsigned long len, enum gacc_mode mode);
|
||||
int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar,
|
||||
unsigned long length, enum gacc_mode mode, u8 access_key);
|
||||
|
||||
int check_gpa_range(struct kvm *kvm, unsigned long gpa, unsigned long length,
|
||||
enum gacc_mode mode, u8 access_key);
|
||||
|
||||
int access_guest_abs_with_key(struct kvm *kvm, gpa_t gpa, void *data,
|
||||
unsigned long len, enum gacc_mode mode, u8 access_key);
|
||||
|
||||
int access_guest_with_key(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
|
||||
void *data, unsigned long len, enum gacc_mode mode,
|
||||
u8 access_key);
|
||||
|
||||
int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
|
||||
void *data, unsigned long len, enum gacc_mode mode);
|
||||
|
||||
/**
|
||||
* write_guest - copy data from kernel space to guest space
|
||||
* write_guest_with_key - copy data from kernel space to guest space
|
||||
* @vcpu: virtual cpu
|
||||
* @ga: guest address
|
||||
* @ar: access register
|
||||
* @data: source address in kernel space
|
||||
* @len: number of bytes to copy
|
||||
* @access_key: access key the storage key needs to match
|
||||
*
|
||||
* Copy @len bytes from @data (kernel space) to @ga (guest address).
|
||||
* In order to copy data to guest space the PSW of the vcpu is inspected:
|
||||
@ -214,8 +224,8 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
|
||||
* The addressing mode of the PSW is also inspected, so that address wrap
|
||||
* around is taken into account for 24-, 31- and 64-bit addressing mode,
|
||||
* if the to be copied data crosses page boundaries in guest address space.
|
||||
* In addition also low address and DAT protection are inspected before
|
||||
* copying any data (key protection is currently not implemented).
|
||||
* In addition low address, DAT and key protection checks are performed before
|
||||
* copying any data.
|
||||
*
|
||||
* This function modifies the 'struct kvm_s390_pgm_info pgm' member of @vcpu.
|
||||
* In case of an access exception (e.g. protection exception) pgm will contain
|
||||
@ -243,10 +253,53 @@ int access_guest_real(struct kvm_vcpu *vcpu, unsigned long gra,
|
||||
* if data has been changed in guest space in case of an exception.
|
||||
*/
|
||||
static inline __must_check
|
||||
int write_guest_with_key(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
|
||||
void *data, unsigned long len, u8 access_key)
|
||||
{
|
||||
return access_guest_with_key(vcpu, ga, ar, data, len, GACC_STORE,
|
||||
access_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* write_guest - copy data from kernel space to guest space
|
||||
* @vcpu: virtual cpu
|
||||
* @ga: guest address
|
||||
* @ar: access register
|
||||
* @data: source address in kernel space
|
||||
* @len: number of bytes to copy
|
||||
*
|
||||
* The behaviour of write_guest is identical to write_guest_with_key, except
|
||||
* that the PSW access key is used instead of an explicit argument.
|
||||
*/
|
||||
static inline __must_check
|
||||
int write_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data,
|
||||
unsigned long len)
|
||||
{
|
||||
return access_guest(vcpu, ga, ar, data, len, GACC_STORE);
|
||||
u8 access_key = psw_bits(vcpu->arch.sie_block->gpsw).key;
|
||||
|
||||
return write_guest_with_key(vcpu, ga, ar, data, len, access_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* read_guest_with_key - copy data from guest space to kernel space
|
||||
* @vcpu: virtual cpu
|
||||
* @ga: guest address
|
||||
* @ar: access register
|
||||
* @data: destination address in kernel space
|
||||
* @len: number of bytes to copy
|
||||
* @access_key: access key the storage key needs to match
|
||||
*
|
||||
* Copy @len bytes from @ga (guest address) to @data (kernel space).
|
||||
*
|
||||
* The behaviour of read_guest_with_key is identical to write_guest_with_key,
|
||||
* except that data will be copied from guest space to kernel space.
|
||||
*/
|
||||
static inline __must_check
|
||||
int read_guest_with_key(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar,
|
||||
void *data, unsigned long len, u8 access_key)
|
||||
{
|
||||
return access_guest_with_key(vcpu, ga, ar, data, len, GACC_FETCH,
|
||||
access_key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -259,14 +312,16 @@ int write_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data,
|
||||
*
|
||||
* Copy @len bytes from @ga (guest address) to @data (kernel space).
|
||||
*
|
||||
* The behaviour of read_guest is identical to write_guest, except that
|
||||
* data will be copied from guest space to kernel space.
|
||||
* The behaviour of read_guest is identical to read_guest_with_key, except
|
||||
* that the PSW access key is used instead of an explicit argument.
|
||||
*/
|
||||
static inline __must_check
|
||||
int read_guest(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, void *data,
|
||||
unsigned long len)
|
||||
{
|
||||
return access_guest(vcpu, ga, ar, data, len, GACC_FETCH);
|
||||
u8 access_key = psw_bits(vcpu->arch.sie_block->gpsw).key;
|
||||
|
||||
return read_guest_with_key(vcpu, ga, ar, data, len, access_key);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -287,7 +342,10 @@ static inline __must_check
|
||||
int read_guest_instr(struct kvm_vcpu *vcpu, unsigned long ga, void *data,
|
||||
unsigned long len)
|
||||
{
|
||||
return access_guest(vcpu, ga, 0, data, len, GACC_IFETCH);
|
||||
u8 access_key = psw_bits(vcpu->arch.sie_block->gpsw).key;
|
||||
|
||||
return access_guest_with_key(vcpu, ga, 0, data, len, GACC_IFETCH,
|
||||
access_key);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -331,18 +331,18 @@ static int handle_mvpg_pei(struct kvm_vcpu *vcpu)
|
||||
|
||||
kvm_s390_get_regs_rre(vcpu, ®1, ®2);
|
||||
|
||||
/* Make sure that the source is paged-in */
|
||||
rc = guest_translate_address(vcpu, vcpu->run->s.regs.gprs[reg2],
|
||||
reg2, &srcaddr, GACC_FETCH);
|
||||
/* Ensure that the source is paged-in, no actual access -> no key checking */
|
||||
rc = guest_translate_address_with_key(vcpu, vcpu->run->s.regs.gprs[reg2],
|
||||
reg2, &srcaddr, GACC_FETCH, 0);
|
||||
if (rc)
|
||||
return kvm_s390_inject_prog_cond(vcpu, rc);
|
||||
rc = kvm_arch_fault_in_page(vcpu, srcaddr, 0);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
/* Make sure that the destination is paged-in */
|
||||
rc = guest_translate_address(vcpu, vcpu->run->s.regs.gprs[reg1],
|
||||
reg1, &dstaddr, GACC_STORE);
|
||||
/* Ensure that the source is paged-in, no actual access -> no key checking */
|
||||
rc = guest_translate_address_with_key(vcpu, vcpu->run->s.regs.gprs[reg1],
|
||||
reg1, &dstaddr, GACC_STORE, 0);
|
||||
if (rc)
|
||||
return kvm_s390_inject_prog_cond(vcpu, rc);
|
||||
rc = kvm_arch_fault_in_page(vcpu, dstaddr, 1);
|
||||
|
@ -564,6 +564,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
|
||||
case KVM_CAP_S390_VCPU_RESETS:
|
||||
case KVM_CAP_SET_GUEST_DEBUG:
|
||||
case KVM_CAP_S390_DIAG318:
|
||||
case KVM_CAP_S390_MEM_OP_EXTENSION:
|
||||
r = 1;
|
||||
break;
|
||||
case KVM_CAP_SET_GUEST_DEBUG2:
|
||||
@ -2359,6 +2360,83 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool access_key_invalid(u8 access_key)
|
||||
{
|
||||
return access_key > 0xf;
|
||||
}
|
||||
|
||||
static int kvm_s390_vm_mem_op(struct kvm *kvm, struct kvm_s390_mem_op *mop)
|
||||
{
|
||||
void __user *uaddr = (void __user *)mop->buf;
|
||||
u64 supported_flags;
|
||||
void *tmpbuf = NULL;
|
||||
int r, srcu_idx;
|
||||
|
||||
supported_flags = KVM_S390_MEMOP_F_SKEY_PROTECTION
|
||||
| KVM_S390_MEMOP_F_CHECK_ONLY;
|
||||
if (mop->flags & ~supported_flags || !mop->size)
|
||||
return -EINVAL;
|
||||
if (mop->size > MEM_OP_MAX_SIZE)
|
||||
return -E2BIG;
|
||||
if (kvm_s390_pv_is_protected(kvm))
|
||||
return -EINVAL;
|
||||
if (mop->flags & KVM_S390_MEMOP_F_SKEY_PROTECTION) {
|
||||
if (access_key_invalid(mop->key))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
mop->key = 0;
|
||||
}
|
||||
if (!(mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY)) {
|
||||
tmpbuf = vmalloc(mop->size);
|
||||
if (!tmpbuf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
srcu_idx = srcu_read_lock(&kvm->srcu);
|
||||
|
||||
if (kvm_is_error_gpa(kvm, mop->gaddr)) {
|
||||
r = PGM_ADDRESSING;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
switch (mop->op) {
|
||||
case KVM_S390_MEMOP_ABSOLUTE_READ: {
|
||||
if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) {
|
||||
r = check_gpa_range(kvm, mop->gaddr, mop->size, GACC_FETCH, mop->key);
|
||||
} else {
|
||||
r = access_guest_abs_with_key(kvm, mop->gaddr, tmpbuf,
|
||||
mop->size, GACC_FETCH, mop->key);
|
||||
if (r == 0) {
|
||||
if (copy_to_user(uaddr, tmpbuf, mop->size))
|
||||
r = -EFAULT;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KVM_S390_MEMOP_ABSOLUTE_WRITE: {
|
||||
if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) {
|
||||
r = check_gpa_range(kvm, mop->gaddr, mop->size, GACC_STORE, mop->key);
|
||||
} else {
|
||||
if (copy_from_user(tmpbuf, uaddr, mop->size)) {
|
||||
r = -EFAULT;
|
||||
break;
|
||||
}
|
||||
r = access_guest_abs_with_key(kvm, mop->gaddr, tmpbuf,
|
||||
mop->size, GACC_STORE, mop->key);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
r = -EINVAL;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
srcu_read_unlock(&kvm->srcu, srcu_idx);
|
||||
|
||||
vfree(tmpbuf);
|
||||
return r;
|
||||
}
|
||||
|
||||
long kvm_arch_vm_ioctl(struct file *filp,
|
||||
unsigned int ioctl, unsigned long arg)
|
||||
{
|
||||
@ -2483,6 +2561,15 @@ long kvm_arch_vm_ioctl(struct file *filp,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KVM_S390_MEM_OP: {
|
||||
struct kvm_s390_mem_op mem_op;
|
||||
|
||||
if (copy_from_user(&mem_op, argp, sizeof(mem_op)) == 0)
|
||||
r = kvm_s390_vm_mem_op(kvm, &mem_op);
|
||||
else
|
||||
r = -EFAULT;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
r = -ENOTTY;
|
||||
}
|
||||
@ -4655,8 +4742,8 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
|
||||
return r;
|
||||
}
|
||||
|
||||
static long kvm_s390_guest_sida_op(struct kvm_vcpu *vcpu,
|
||||
struct kvm_s390_mem_op *mop)
|
||||
static long kvm_s390_vcpu_sida_op(struct kvm_vcpu *vcpu,
|
||||
struct kvm_s390_mem_op *mop)
|
||||
{
|
||||
void __user *uaddr = (void __user *)mop->buf;
|
||||
int r = 0;
|
||||
@ -4667,6 +4754,8 @@ static long kvm_s390_guest_sida_op(struct kvm_vcpu *vcpu,
|
||||
return -EINVAL;
|
||||
if (mop->size + mop->sida_offset > sida_size(vcpu->arch.sie_block))
|
||||
return -E2BIG;
|
||||
if (!kvm_s390_pv_cpu_is_protected(vcpu))
|
||||
return -EINVAL;
|
||||
|
||||
switch (mop->op) {
|
||||
case KVM_S390_MEMOP_SIDA_READ:
|
||||
@ -4683,24 +4772,29 @@ static long kvm_s390_guest_sida_op(struct kvm_vcpu *vcpu,
|
||||
}
|
||||
return r;
|
||||
}
|
||||
static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu,
|
||||
struct kvm_s390_mem_op *mop)
|
||||
|
||||
static long kvm_s390_vcpu_mem_op(struct kvm_vcpu *vcpu,
|
||||
struct kvm_s390_mem_op *mop)
|
||||
{
|
||||
void __user *uaddr = (void __user *)mop->buf;
|
||||
void *tmpbuf = NULL;
|
||||
int r = 0;
|
||||
const u64 supported_flags = KVM_S390_MEMOP_F_INJECT_EXCEPTION
|
||||
| KVM_S390_MEMOP_F_CHECK_ONLY;
|
||||
| KVM_S390_MEMOP_F_CHECK_ONLY
|
||||
| KVM_S390_MEMOP_F_SKEY_PROTECTION;
|
||||
|
||||
if (mop->flags & ~supported_flags || mop->ar >= NUM_ACRS || !mop->size)
|
||||
return -EINVAL;
|
||||
|
||||
if (mop->size > MEM_OP_MAX_SIZE)
|
||||
return -E2BIG;
|
||||
|
||||
if (kvm_s390_pv_cpu_is_protected(vcpu))
|
||||
return -EINVAL;
|
||||
|
||||
if (mop->flags & KVM_S390_MEMOP_F_SKEY_PROTECTION) {
|
||||
if (access_key_invalid(mop->key))
|
||||
return -EINVAL;
|
||||
} else {
|
||||
mop->key = 0;
|
||||
}
|
||||
if (!(mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY)) {
|
||||
tmpbuf = vmalloc(mop->size);
|
||||
if (!tmpbuf)
|
||||
@ -4710,11 +4804,12 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu,
|
||||
switch (mop->op) {
|
||||
case KVM_S390_MEMOP_LOGICAL_READ:
|
||||
if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) {
|
||||
r = check_gva_range(vcpu, mop->gaddr, mop->ar,
|
||||
mop->size, GACC_FETCH);
|
||||
r = check_gva_range(vcpu, mop->gaddr, mop->ar, mop->size,
|
||||
GACC_FETCH, mop->key);
|
||||
break;
|
||||
}
|
||||
r = read_guest(vcpu, mop->gaddr, mop->ar, tmpbuf, mop->size);
|
||||
r = read_guest_with_key(vcpu, mop->gaddr, mop->ar, tmpbuf,
|
||||
mop->size, mop->key);
|
||||
if (r == 0) {
|
||||
if (copy_to_user(uaddr, tmpbuf, mop->size))
|
||||
r = -EFAULT;
|
||||
@ -4722,15 +4817,16 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu,
|
||||
break;
|
||||
case KVM_S390_MEMOP_LOGICAL_WRITE:
|
||||
if (mop->flags & KVM_S390_MEMOP_F_CHECK_ONLY) {
|
||||
r = check_gva_range(vcpu, mop->gaddr, mop->ar,
|
||||
mop->size, GACC_STORE);
|
||||
r = check_gva_range(vcpu, mop->gaddr, mop->ar, mop->size,
|
||||
GACC_STORE, mop->key);
|
||||
break;
|
||||
}
|
||||
if (copy_from_user(tmpbuf, uaddr, mop->size)) {
|
||||
r = -EFAULT;
|
||||
break;
|
||||
}
|
||||
r = write_guest(vcpu, mop->gaddr, mop->ar, tmpbuf, mop->size);
|
||||
r = write_guest_with_key(vcpu, mop->gaddr, mop->ar, tmpbuf,
|
||||
mop->size, mop->key);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -4741,8 +4837,8 @@ static long kvm_s390_guest_mem_op(struct kvm_vcpu *vcpu,
|
||||
return r;
|
||||
}
|
||||
|
||||
static long kvm_s390_guest_memsida_op(struct kvm_vcpu *vcpu,
|
||||
struct kvm_s390_mem_op *mop)
|
||||
static long kvm_s390_vcpu_memsida_op(struct kvm_vcpu *vcpu,
|
||||
struct kvm_s390_mem_op *mop)
|
||||
{
|
||||
int r, srcu_idx;
|
||||
|
||||
@ -4751,12 +4847,12 @@ static long kvm_s390_guest_memsida_op(struct kvm_vcpu *vcpu,
|
||||
switch (mop->op) {
|
||||
case KVM_S390_MEMOP_LOGICAL_READ:
|
||||
case KVM_S390_MEMOP_LOGICAL_WRITE:
|
||||
r = kvm_s390_guest_mem_op(vcpu, mop);
|
||||
r = kvm_s390_vcpu_mem_op(vcpu, mop);
|
||||
break;
|
||||
case KVM_S390_MEMOP_SIDA_READ:
|
||||
case KVM_S390_MEMOP_SIDA_WRITE:
|
||||
/* we are locked against sida going away by the vcpu->mutex */
|
||||
r = kvm_s390_guest_sida_op(vcpu, mop);
|
||||
r = kvm_s390_vcpu_sida_op(vcpu, mop);
|
||||
break;
|
||||
default:
|
||||
r = -EINVAL;
|
||||
@ -4919,7 +5015,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
|
||||
struct kvm_s390_mem_op mem_op;
|
||||
|
||||
if (copy_from_user(&mem_op, argp, sizeof(mem_op)) == 0)
|
||||
r = kvm_s390_guest_memsida_op(vcpu, &mem_op);
|
||||
r = kvm_s390_vcpu_memsida_op(vcpu, &mem_op);
|
||||
else
|
||||
r = -EFAULT;
|
||||
break;
|
||||
|
@ -1443,10 +1443,11 @@ int kvm_s390_handle_eb(struct kvm_vcpu *vcpu)
|
||||
|
||||
static int handle_tprot(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
u64 address1, address2;
|
||||
unsigned long hva, gpa;
|
||||
int ret = 0, cc = 0;
|
||||
u64 address, operand2;
|
||||
unsigned long gpa;
|
||||
u8 access_key;
|
||||
bool writable;
|
||||
int ret, cc;
|
||||
u8 ar;
|
||||
|
||||
vcpu->stat.instruction_tprot++;
|
||||
@ -1454,43 +1455,46 @@ static int handle_tprot(struct kvm_vcpu *vcpu)
|
||||
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE)
|
||||
return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP);
|
||||
|
||||
kvm_s390_get_base_disp_sse(vcpu, &address1, &address2, &ar, NULL);
|
||||
kvm_s390_get_base_disp_sse(vcpu, &address, &operand2, &ar, NULL);
|
||||
access_key = (operand2 & 0xf0) >> 4;
|
||||
|
||||
/* we only handle the Linux memory detection case:
|
||||
* access key == 0
|
||||
* everything else goes to userspace. */
|
||||
if (address2 & 0xf0)
|
||||
return -EOPNOTSUPP;
|
||||
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT)
|
||||
ipte_lock(vcpu);
|
||||
ret = guest_translate_address(vcpu, address1, ar, &gpa, GACC_STORE);
|
||||
if (ret == PGM_PROTECTION) {
|
||||
|
||||
ret = guest_translate_address_with_key(vcpu, address, ar, &gpa,
|
||||
GACC_STORE, access_key);
|
||||
if (ret == 0) {
|
||||
gfn_to_hva_prot(vcpu->kvm, gpa_to_gfn(gpa), &writable);
|
||||
} else if (ret == PGM_PROTECTION) {
|
||||
writable = false;
|
||||
/* Write protected? Try again with read-only... */
|
||||
cc = 1;
|
||||
ret = guest_translate_address(vcpu, address1, ar, &gpa,
|
||||
GACC_FETCH);
|
||||
ret = guest_translate_address_with_key(vcpu, address, ar, &gpa,
|
||||
GACC_FETCH, access_key);
|
||||
}
|
||||
if (ret) {
|
||||
if (ret == PGM_ADDRESSING || ret == PGM_TRANSLATION_SPEC) {
|
||||
ret = kvm_s390_inject_program_int(vcpu, ret);
|
||||
} else if (ret > 0) {
|
||||
/* Translation not available */
|
||||
kvm_s390_set_psw_cc(vcpu, 3);
|
||||
if (ret >= 0) {
|
||||
cc = -1;
|
||||
|
||||
/* Fetching permitted; storing permitted */
|
||||
if (ret == 0 && writable)
|
||||
cc = 0;
|
||||
/* Fetching permitted; storing not permitted */
|
||||
else if (ret == 0 && !writable)
|
||||
cc = 1;
|
||||
/* Fetching not permitted; storing not permitted */
|
||||
else if (ret == PGM_PROTECTION)
|
||||
cc = 2;
|
||||
/* Translation not available */
|
||||
else if (ret != PGM_ADDRESSING && ret != PGM_TRANSLATION_SPEC)
|
||||
cc = 3;
|
||||
|
||||
if (cc != -1) {
|
||||
kvm_s390_set_psw_cc(vcpu, cc);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = kvm_s390_inject_program_int(vcpu, ret);
|
||||
}
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
hva = gfn_to_hva_prot(vcpu->kvm, gpa_to_gfn(gpa), &writable);
|
||||
if (kvm_is_error_hva(hva)) {
|
||||
ret = kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
|
||||
} else {
|
||||
if (!writable)
|
||||
cc = 1; /* Write not permitted ==> read-only */
|
||||
kvm_s390_set_psw_cc(vcpu, cc);
|
||||
/* Note: CC2 only occurs for storage keys (not supported yet) */
|
||||
}
|
||||
out_unlock:
|
||||
if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT)
|
||||
ipte_unlock(vcpu);
|
||||
return ret;
|
||||
|
@ -59,11 +59,13 @@ static inline int copy_with_mvcos(void)
|
||||
#endif
|
||||
|
||||
static inline unsigned long copy_from_user_mvcos(void *x, const void __user *ptr,
|
||||
unsigned long size)
|
||||
unsigned long size, unsigned long key)
|
||||
{
|
||||
unsigned long tmp1, tmp2;
|
||||
union oac spec = {
|
||||
.oac2.key = key,
|
||||
.oac2.as = PSW_BITS_AS_SECONDARY,
|
||||
.oac2.k = 1,
|
||||
.oac2.a = 1,
|
||||
};
|
||||
|
||||
@ -94,19 +96,19 @@ static inline unsigned long copy_from_user_mvcos(void *x, const void __user *ptr
|
||||
}
|
||||
|
||||
static inline unsigned long copy_from_user_mvcp(void *x, const void __user *ptr,
|
||||
unsigned long size)
|
||||
unsigned long size, unsigned long key)
|
||||
{
|
||||
unsigned long tmp1, tmp2;
|
||||
|
||||
tmp1 = -256UL;
|
||||
asm volatile(
|
||||
" sacf 0\n"
|
||||
"0: mvcp 0(%0,%2),0(%1),%3\n"
|
||||
"0: mvcp 0(%0,%2),0(%1),%[key]\n"
|
||||
"7: jz 5f\n"
|
||||
"1: algr %0,%3\n"
|
||||
" la %1,256(%1)\n"
|
||||
" la %2,256(%2)\n"
|
||||
"2: mvcp 0(%0,%2),0(%1),%3\n"
|
||||
"2: mvcp 0(%0,%2),0(%1),%[key]\n"
|
||||
"8: jnz 1b\n"
|
||||
" j 5f\n"
|
||||
"3: la %4,255(%1)\n" /* %4 = ptr + 255 */
|
||||
@ -115,7 +117,7 @@ static inline unsigned long copy_from_user_mvcp(void *x, const void __user *ptr,
|
||||
" slgr %4,%1\n"
|
||||
" clgr %0,%4\n" /* copy crosses next page boundary? */
|
||||
" jnh 6f\n"
|
||||
"4: mvcp 0(%4,%2),0(%1),%3\n"
|
||||
"4: mvcp 0(%4,%2),0(%1),%[key]\n"
|
||||
"9: slgr %0,%4\n"
|
||||
" j 6f\n"
|
||||
"5: slgr %0,%0\n"
|
||||
@ -123,24 +125,49 @@ static inline unsigned long copy_from_user_mvcp(void *x, const void __user *ptr,
|
||||
EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b)
|
||||
EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b)
|
||||
: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
|
||||
: : "cc", "memory");
|
||||
: [key] "d" (key << 4)
|
||||
: "cc", "memory");
|
||||
return size;
|
||||
}
|
||||
|
||||
static unsigned long raw_copy_from_user_key(void *to, const void __user *from,
|
||||
unsigned long n, unsigned long key)
|
||||
{
|
||||
if (copy_with_mvcos())
|
||||
return copy_from_user_mvcos(to, from, n, key);
|
||||
return copy_from_user_mvcp(to, from, n, key);
|
||||
}
|
||||
|
||||
unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n)
|
||||
{
|
||||
if (copy_with_mvcos())
|
||||
return copy_from_user_mvcos(to, from, n);
|
||||
return copy_from_user_mvcp(to, from, n);
|
||||
return raw_copy_from_user_key(to, from, n, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(raw_copy_from_user);
|
||||
|
||||
unsigned long _copy_from_user_key(void *to, const void __user *from,
|
||||
unsigned long n, unsigned long key)
|
||||
{
|
||||
unsigned long res = n;
|
||||
|
||||
might_fault();
|
||||
if (!should_fail_usercopy()) {
|
||||
instrument_copy_from_user(to, from, n);
|
||||
res = raw_copy_from_user_key(to, from, n, key);
|
||||
}
|
||||
if (unlikely(res))
|
||||
memset(to + (n - res), 0, res);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(_copy_from_user_key);
|
||||
|
||||
static inline unsigned long copy_to_user_mvcos(void __user *ptr, const void *x,
|
||||
unsigned long size)
|
||||
unsigned long size, unsigned long key)
|
||||
{
|
||||
unsigned long tmp1, tmp2;
|
||||
union oac spec = {
|
||||
.oac1.key = key,
|
||||
.oac1.as = PSW_BITS_AS_SECONDARY,
|
||||
.oac1.k = 1,
|
||||
.oac1.a = 1,
|
||||
};
|
||||
|
||||
@ -171,19 +198,19 @@ static inline unsigned long copy_to_user_mvcos(void __user *ptr, const void *x,
|
||||
}
|
||||
|
||||
static inline unsigned long copy_to_user_mvcs(void __user *ptr, const void *x,
|
||||
unsigned long size)
|
||||
unsigned long size, unsigned long key)
|
||||
{
|
||||
unsigned long tmp1, tmp2;
|
||||
|
||||
tmp1 = -256UL;
|
||||
asm volatile(
|
||||
" sacf 0\n"
|
||||
"0: mvcs 0(%0,%1),0(%2),%3\n"
|
||||
"0: mvcs 0(%0,%1),0(%2),%[key]\n"
|
||||
"7: jz 5f\n"
|
||||
"1: algr %0,%3\n"
|
||||
" la %1,256(%1)\n"
|
||||
" la %2,256(%2)\n"
|
||||
"2: mvcs 0(%0,%1),0(%2),%3\n"
|
||||
"2: mvcs 0(%0,%1),0(%2),%[key]\n"
|
||||
"8: jnz 1b\n"
|
||||
" j 5f\n"
|
||||
"3: la %4,255(%1)\n" /* %4 = ptr + 255 */
|
||||
@ -192,7 +219,7 @@ static inline unsigned long copy_to_user_mvcs(void __user *ptr, const void *x,
|
||||
" slgr %4,%1\n"
|
||||
" clgr %0,%4\n" /* copy crosses next page boundary? */
|
||||
" jnh 6f\n"
|
||||
"4: mvcs 0(%4,%1),0(%2),%3\n"
|
||||
"4: mvcs 0(%4,%1),0(%2),%[key]\n"
|
||||
"9: slgr %0,%4\n"
|
||||
" j 6f\n"
|
||||
"5: slgr %0,%0\n"
|
||||
@ -200,18 +227,36 @@ static inline unsigned long copy_to_user_mvcs(void __user *ptr, const void *x,
|
||||
EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b)
|
||||
EX_TABLE(7b,3b) EX_TABLE(8b,3b) EX_TABLE(9b,6b)
|
||||
: "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
|
||||
: : "cc", "memory");
|
||||
: [key] "d" (key << 4)
|
||||
: "cc", "memory");
|
||||
return size;
|
||||
}
|
||||
|
||||
static unsigned long raw_copy_to_user_key(void __user *to, const void *from,
|
||||
unsigned long n, unsigned long key)
|
||||
{
|
||||
if (copy_with_mvcos())
|
||||
return copy_to_user_mvcos(to, from, n, key);
|
||||
return copy_to_user_mvcs(to, from, n, key);
|
||||
}
|
||||
|
||||
unsigned long raw_copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||
{
|
||||
if (copy_with_mvcos())
|
||||
return copy_to_user_mvcos(to, from, n);
|
||||
return copy_to_user_mvcs(to, from, n);
|
||||
return raw_copy_to_user_key(to, from, n, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(raw_copy_to_user);
|
||||
|
||||
unsigned long _copy_to_user_key(void __user *to, const void *from,
|
||||
unsigned long n, unsigned long key)
|
||||
{
|
||||
might_fault();
|
||||
if (should_fail_usercopy())
|
||||
return n;
|
||||
instrument_copy_to_user(to, from, n);
|
||||
return raw_copy_to_user_key(to, from, n, key);
|
||||
}
|
||||
EXPORT_SYMBOL(_copy_to_user_key);
|
||||
|
||||
static inline unsigned long clear_user_mvcos(void __user *to, unsigned long size)
|
||||
{
|
||||
unsigned long tmp1, tmp2;
|
||||
|
@ -2448,23 +2448,21 @@ static void ata_dev_config_cpr(struct ata_device *dev)
|
||||
struct ata_cpr_log *cpr_log = NULL;
|
||||
u8 *desc, *buf = NULL;
|
||||
|
||||
if (!ata_identify_page_supported(dev,
|
||||
ATA_LOG_CONCURRENT_POSITIONING_RANGES))
|
||||
if (ata_id_major_version(dev->id) < 11 ||
|
||||
!ata_log_supported(dev, ATA_LOG_CONCURRENT_POSITIONING_RANGES))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Read IDENTIFY DEVICE data log, page 0x47
|
||||
* (concurrent positioning ranges). We can have at most 255 32B range
|
||||
* descriptors plus a 64B header.
|
||||
* Read the concurrent positioning ranges log (0x47). We can have at
|
||||
* most 255 32B range descriptors plus a 64B header.
|
||||
*/
|
||||
buf_len = (64 + 255 * 32 + 511) & ~511;
|
||||
buf = kzalloc(buf_len, GFP_KERNEL);
|
||||
if (!buf)
|
||||
goto out;
|
||||
|
||||
err_mask = ata_read_log_page(dev, ATA_LOG_IDENTIFY_DEVICE,
|
||||
ATA_LOG_CONCURRENT_POSITIONING_RANGES,
|
||||
buf, buf_len >> 9);
|
||||
err_mask = ata_read_log_page(dev, ATA_LOG_CONCURRENT_POSITIONING_RANGES,
|
||||
0, buf, buf_len >> 9);
|
||||
if (err_mask)
|
||||
goto out;
|
||||
|
||||
|
@ -67,7 +67,7 @@ static const unsigned int sd_au_size[] = {
|
||||
__res & __mask; \
|
||||
})
|
||||
|
||||
#define SD_POWEROFF_NOTIFY_TIMEOUT_MS 2000
|
||||
#define SD_POWEROFF_NOTIFY_TIMEOUT_MS 1000
|
||||
#define SD_WRITE_EXTR_SINGLE_TIMEOUT_MS 1000
|
||||
|
||||
struct sd_busy_data {
|
||||
@ -1664,6 +1664,12 @@ static int sd_poweroff_notify(struct mmc_card *card)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Find out when the command is completed. */
|
||||
err = mmc_poll_for_busy(card, SD_WRITE_EXTR_SINGLE_TIMEOUT_MS, false,
|
||||
MMC_BUSY_EXTR_SINGLE);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
cb_data.card = card;
|
||||
cb_data.reg_buf = reg_buf;
|
||||
err = __mmc_poll_for_busy(card->host, SD_POWEROFF_NOTIFY_TIMEOUT_MS,
|
||||
|
@ -705,12 +705,12 @@ static int moxart_remove(struct platform_device *pdev)
|
||||
if (!IS_ERR_OR_NULL(host->dma_chan_rx))
|
||||
dma_release_channel(host->dma_chan_rx);
|
||||
mmc_remove_host(mmc);
|
||||
mmc_free_host(mmc);
|
||||
|
||||
writel(0, host->base + REG_INTERRUPT_MASK);
|
||||
writel(0, host->base + REG_POWER_CONTROL);
|
||||
writel(readl(host->base + REG_CLOCK_CONTROL) | CLK_OFF,
|
||||
host->base + REG_CLOCK_CONTROL);
|
||||
mmc_free_host(mmc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -524,12 +524,16 @@ static void esdhc_of_adma_workaround(struct sdhci_host *host, u32 intmask)
|
||||
|
||||
static int esdhc_of_enable_dma(struct sdhci_host *host)
|
||||
{
|
||||
int ret;
|
||||
u32 value;
|
||||
struct device *dev = mmc_dev(host->mmc);
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "fsl,ls1043a-esdhc") ||
|
||||
of_device_is_compatible(dev->of_node, "fsl,ls1046a-esdhc"))
|
||||
dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
|
||||
of_device_is_compatible(dev->of_node, "fsl,ls1046a-esdhc")) {
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
value = sdhci_readl(host, ESDHC_DMA_SYSCTL);
|
||||
|
||||
|
@ -405,6 +405,9 @@ static int sh_mmcif_dma_slave_config(struct sh_mmcif_host *host,
|
||||
struct dma_slave_config cfg = { 0, };
|
||||
|
||||
res = platform_get_resource(host->pd, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
|
||||
cfg.direction = direction;
|
||||
|
||||
if (direction == DMA_DEV_TO_MEM) {
|
||||
|
@ -369,8 +369,8 @@ source "fs/ksmbd/Kconfig"
|
||||
|
||||
config SMBFS_COMMON
|
||||
tristate
|
||||
default y if CIFS=y
|
||||
default m if CIFS=m
|
||||
default y if CIFS=y || SMB_SERVER=y
|
||||
default m if CIFS=m || SMB_SERVER=m
|
||||
|
||||
source "fs/coda/Kconfig"
|
||||
source "fs/afs/Kconfig"
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "mgmt/user_config.h"
|
||||
#include "crypto_ctx.h"
|
||||
#include "transport_ipc.h"
|
||||
#include "../smbfs_common/arc4.h"
|
||||
|
||||
/*
|
||||
* Fixed format data defining GSS header and fixed string
|
||||
@ -336,6 +337,29 @@ int ksmbd_decode_ntlmssp_auth_blob(struct authenticate_message *authblob,
|
||||
nt_len - CIFS_ENCPWD_SIZE,
|
||||
domain_name, conn->ntlmssp.cryptkey);
|
||||
kfree(domain_name);
|
||||
|
||||
/* The recovered secondary session key */
|
||||
if (conn->ntlmssp.client_flags & NTLMSSP_NEGOTIATE_KEY_XCH) {
|
||||
struct arc4_ctx *ctx_arc4;
|
||||
unsigned int sess_key_off, sess_key_len;
|
||||
|
||||
sess_key_off = le32_to_cpu(authblob->SessionKey.BufferOffset);
|
||||
sess_key_len = le16_to_cpu(authblob->SessionKey.Length);
|
||||
|
||||
if (blob_len < (u64)sess_key_off + sess_key_len)
|
||||
return -EINVAL;
|
||||
|
||||
ctx_arc4 = kmalloc(sizeof(*ctx_arc4), GFP_KERNEL);
|
||||
if (!ctx_arc4)
|
||||
return -ENOMEM;
|
||||
|
||||
cifs_arc4_setkey(ctx_arc4, sess->sess_key,
|
||||
SMB2_NTLMV2_SESSKEY_SIZE);
|
||||
cifs_arc4_crypt(ctx_arc4, sess->sess_key,
|
||||
(char *)authblob + sess_key_off, sess_key_len);
|
||||
kfree_sensitive(ctx_arc4);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -408,6 +432,9 @@ ksmbd_build_ntlmssp_challenge_blob(struct challenge_message *chgblob,
|
||||
(cflags & NTLMSSP_NEGOTIATE_EXTENDED_SEC))
|
||||
flags |= NTLMSSP_NEGOTIATE_EXTENDED_SEC;
|
||||
|
||||
if (cflags & NTLMSSP_NEGOTIATE_KEY_XCH)
|
||||
flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
|
||||
|
||||
chgblob->NegotiateFlags = cpu_to_le32(flags);
|
||||
len = strlen(ksmbd_netbios_name());
|
||||
name = kmalloc(2 + UNICODE_LEN(len), GFP_KERNEL);
|
||||
|
@ -2688,7 +2688,7 @@ int smb2_open(struct ksmbd_work *work)
|
||||
(struct create_posix *)context;
|
||||
if (le16_to_cpu(context->DataOffset) +
|
||||
le32_to_cpu(context->DataLength) <
|
||||
sizeof(struct create_posix)) {
|
||||
sizeof(struct create_posix) - 4) {
|
||||
rc = -EINVAL;
|
||||
goto err_out1;
|
||||
}
|
||||
@ -3422,9 +3422,9 @@ static int smb2_populate_readdir_entry(struct ksmbd_conn *conn, int info_level,
|
||||
goto free_conv_name;
|
||||
}
|
||||
|
||||
struct_sz = readdir_info_level_struct_sz(info_level);
|
||||
next_entry_offset = ALIGN(struct_sz - 1 + conv_len,
|
||||
KSMBD_DIR_INFO_ALIGNMENT);
|
||||
struct_sz = readdir_info_level_struct_sz(info_level) - 1 + conv_len;
|
||||
next_entry_offset = ALIGN(struct_sz, KSMBD_DIR_INFO_ALIGNMENT);
|
||||
d_info->last_entry_off_align = next_entry_offset - struct_sz;
|
||||
|
||||
if (next_entry_offset > d_info->out_buf_len) {
|
||||
d_info->out_buf_len = 0;
|
||||
@ -3976,6 +3976,7 @@ int smb2_query_dir(struct ksmbd_work *work)
|
||||
((struct file_directory_info *)
|
||||
((char *)rsp->Buffer + d_info.last_entry_offset))
|
||||
->NextEntryOffset = 0;
|
||||
d_info.data_count -= d_info.last_entry_off_align;
|
||||
|
||||
rsp->StructureSize = cpu_to_le16(9);
|
||||
rsp->OutputBufferOffset = cpu_to_le16(72);
|
||||
@ -6126,13 +6127,26 @@ static int smb2_set_remote_key_for_rdma(struct ksmbd_work *work,
|
||||
__le16 ChannelInfoOffset,
|
||||
__le16 ChannelInfoLength)
|
||||
{
|
||||
unsigned int i, ch_count;
|
||||
|
||||
if (work->conn->dialect == SMB30_PROT_ID &&
|
||||
Channel != SMB2_CHANNEL_RDMA_V1)
|
||||
return -EINVAL;
|
||||
|
||||
if (ChannelInfoOffset == 0 ||
|
||||
le16_to_cpu(ChannelInfoLength) < sizeof(*desc))
|
||||
ch_count = le16_to_cpu(ChannelInfoLength) / sizeof(*desc);
|
||||
if (ksmbd_debug_types & KSMBD_DEBUG_RDMA) {
|
||||
for (i = 0; i < ch_count; i++) {
|
||||
pr_info("RDMA r/w request %#x: token %#x, length %#x\n",
|
||||
i,
|
||||
le32_to_cpu(desc[i].token),
|
||||
le32_to_cpu(desc[i].length));
|
||||
}
|
||||
}
|
||||
if (ch_count != 1) {
|
||||
ksmbd_debug(RDMA, "RDMA multiple buffer descriptors %d are not supported yet\n",
|
||||
ch_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
work->need_invalidate_rkey =
|
||||
(Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE);
|
||||
@ -6185,9 +6199,15 @@ int smb2_read(struct ksmbd_work *work)
|
||||
|
||||
if (req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE ||
|
||||
req->Channel == SMB2_CHANNEL_RDMA_V1) {
|
||||
unsigned int ch_offset = le16_to_cpu(req->ReadChannelInfoOffset);
|
||||
|
||||
if (ch_offset < offsetof(struct smb2_read_req, Buffer)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
err = smb2_set_remote_key_for_rdma(work,
|
||||
(struct smb2_buffer_desc_v1 *)
|
||||
&req->Buffer[0],
|
||||
((char *)req + ch_offset),
|
||||
req->Channel,
|
||||
req->ReadChannelInfoOffset,
|
||||
req->ReadChannelInfoLength);
|
||||
@ -6428,11 +6448,16 @@ int smb2_write(struct ksmbd_work *work)
|
||||
|
||||
if (req->Channel == SMB2_CHANNEL_RDMA_V1 ||
|
||||
req->Channel == SMB2_CHANNEL_RDMA_V1_INVALIDATE) {
|
||||
if (req->Length != 0 || req->DataOffset != 0)
|
||||
return -EINVAL;
|
||||
unsigned int ch_offset = le16_to_cpu(req->WriteChannelInfoOffset);
|
||||
|
||||
if (req->Length != 0 || req->DataOffset != 0 ||
|
||||
ch_offset < offsetof(struct smb2_write_req, Buffer)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
err = smb2_set_remote_key_for_rdma(work,
|
||||
(struct smb2_buffer_desc_v1 *)
|
||||
&req->Buffer[0],
|
||||
((char *)req + ch_offset),
|
||||
req->Channel,
|
||||
req->WriteChannelInfoOffset,
|
||||
req->WriteChannelInfoLength);
|
||||
|
@ -308,14 +308,17 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level,
|
||||
for (i = 0; i < 2; i++) {
|
||||
struct kstat kstat;
|
||||
struct ksmbd_kstat ksmbd_kstat;
|
||||
struct dentry *dentry;
|
||||
|
||||
if (!dir->dot_dotdot[i]) { /* fill dot entry info */
|
||||
if (i == 0) {
|
||||
d_info->name = ".";
|
||||
d_info->name_len = 1;
|
||||
dentry = dir->filp->f_path.dentry;
|
||||
} else {
|
||||
d_info->name = "..";
|
||||
d_info->name_len = 2;
|
||||
dentry = dir->filp->f_path.dentry->d_parent;
|
||||
}
|
||||
|
||||
if (!match_pattern(d_info->name, d_info->name_len,
|
||||
@ -327,7 +330,7 @@ int ksmbd_populate_dot_dotdot_entries(struct ksmbd_work *work, int info_level,
|
||||
ksmbd_kstat.kstat = &kstat;
|
||||
ksmbd_vfs_fill_dentry_attrs(work,
|
||||
user_ns,
|
||||
dir->filp->f_path.dentry->d_parent,
|
||||
dentry,
|
||||
&ksmbd_kstat);
|
||||
rc = fn(conn, info_level, d_info, &ksmbd_kstat);
|
||||
if (rc)
|
||||
|
@ -80,7 +80,7 @@ static int smb_direct_max_fragmented_recv_size = 1024 * 1024;
|
||||
/* The maximum single-message size which can be received */
|
||||
static int smb_direct_max_receive_size = 8192;
|
||||
|
||||
static int smb_direct_max_read_write_size = 1048512;
|
||||
static int smb_direct_max_read_write_size = 524224;
|
||||
|
||||
static int smb_direct_max_outstanding_rw_ops = 8;
|
||||
|
||||
|
@ -47,6 +47,7 @@ struct ksmbd_dir_info {
|
||||
int last_entry_offset;
|
||||
bool hide_dot_file;
|
||||
int flags;
|
||||
int last_entry_off_align;
|
||||
};
|
||||
|
||||
struct ksmbd_readdir_data {
|
||||
|
@ -177,6 +177,7 @@ struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_init)
|
||||
INIT_LIST_HEAD(&clp->cl_superblocks);
|
||||
clp->cl_rpcclient = ERR_PTR(-EINVAL);
|
||||
|
||||
clp->cl_flags = cl_init->init_flags;
|
||||
clp->cl_proto = cl_init->proto;
|
||||
clp->cl_nconnect = cl_init->nconnect;
|
||||
clp->cl_max_connect = cl_init->max_connect ? cl_init->max_connect : 1;
|
||||
@ -423,7 +424,6 @@ struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init)
|
||||
list_add_tail(&new->cl_share_link,
|
||||
&nn->nfs_client_list);
|
||||
spin_unlock(&nn->nfs_client_lock);
|
||||
new->cl_flags = cl_init->init_flags;
|
||||
return rpc_ops->init_client(new, cl_init);
|
||||
}
|
||||
|
||||
|
24
fs/nfs/dir.c
24
fs/nfs/dir.c
@ -80,6 +80,7 @@ static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir
|
||||
ctx->dir_cookie = 0;
|
||||
ctx->dup_cookie = 0;
|
||||
ctx->page_index = 0;
|
||||
ctx->eof = false;
|
||||
spin_lock(&dir->i_lock);
|
||||
if (list_empty(&nfsi->open_files) &&
|
||||
(nfsi->cache_validity & NFS_INO_DATA_INVAL_DEFER))
|
||||
@ -168,6 +169,7 @@ struct nfs_readdir_descriptor {
|
||||
unsigned int cache_entry_index;
|
||||
signed char duped;
|
||||
bool plus;
|
||||
bool eob;
|
||||
bool eof;
|
||||
};
|
||||
|
||||
@ -867,7 +869,8 @@ static int nfs_readdir_xdr_to_array(struct nfs_readdir_descriptor *desc,
|
||||
|
||||
status = nfs_readdir_page_filler(desc, entry, pages, pglen,
|
||||
arrays, narrays);
|
||||
} while (!status && nfs_readdir_page_needs_filling(page));
|
||||
} while (!status && nfs_readdir_page_needs_filling(page) &&
|
||||
page_mapping(page));
|
||||
|
||||
nfs_readdir_free_pages(pages, array_size);
|
||||
out:
|
||||
@ -988,7 +991,7 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
|
||||
ent = &array->array[i];
|
||||
if (!dir_emit(desc->ctx, ent->name, ent->name_len,
|
||||
nfs_compat_user_ino64(ent->ino), ent->d_type)) {
|
||||
desc->eof = true;
|
||||
desc->eob = true;
|
||||
break;
|
||||
}
|
||||
memcpy(desc->verf, verf, sizeof(desc->verf));
|
||||
@ -1004,7 +1007,7 @@ static void nfs_do_filldir(struct nfs_readdir_descriptor *desc,
|
||||
desc->duped = 1;
|
||||
}
|
||||
if (array->page_is_eof)
|
||||
desc->eof = true;
|
||||
desc->eof = !desc->eob;
|
||||
|
||||
kunmap(desc->page);
|
||||
dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %llu\n",
|
||||
@ -1041,12 +1044,13 @@ static int uncached_readdir(struct nfs_readdir_descriptor *desc)
|
||||
goto out;
|
||||
|
||||
desc->page_index = 0;
|
||||
desc->cache_entry_index = 0;
|
||||
desc->last_cookie = desc->dir_cookie;
|
||||
desc->duped = 0;
|
||||
|
||||
status = nfs_readdir_xdr_to_array(desc, desc->verf, verf, arrays, sz);
|
||||
|
||||
for (i = 0; !desc->eof && i < sz && arrays[i]; i++) {
|
||||
for (i = 0; !desc->eob && i < sz && arrays[i]; i++) {
|
||||
desc->page = arrays[i];
|
||||
nfs_do_filldir(desc, verf);
|
||||
}
|
||||
@ -1105,9 +1109,15 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
desc->duped = dir_ctx->duped;
|
||||
page_index = dir_ctx->page_index;
|
||||
desc->attr_gencount = dir_ctx->attr_gencount;
|
||||
desc->eof = dir_ctx->eof;
|
||||
memcpy(desc->verf, dir_ctx->verf, sizeof(desc->verf));
|
||||
spin_unlock(&file->f_lock);
|
||||
|
||||
if (desc->eof) {
|
||||
res = 0;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(NFS_INO_FORCE_READDIR, &nfsi->flags) &&
|
||||
list_is_singular(&nfsi->open_files))
|
||||
invalidate_mapping_pages(inode->i_mapping, page_index + 1, -1);
|
||||
@ -1141,7 +1151,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
|
||||
nfs_do_filldir(desc, nfsi->cookieverf);
|
||||
nfs_readdir_page_unlock_and_put_cached(desc);
|
||||
} while (!desc->eof);
|
||||
} while (!desc->eob && !desc->eof);
|
||||
|
||||
spin_lock(&file->f_lock);
|
||||
dir_ctx->dir_cookie = desc->dir_cookie;
|
||||
@ -1149,9 +1159,10 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
|
||||
dir_ctx->duped = desc->duped;
|
||||
dir_ctx->attr_gencount = desc->attr_gencount;
|
||||
dir_ctx->page_index = desc->page_index;
|
||||
dir_ctx->eof = desc->eof;
|
||||
memcpy(dir_ctx->verf, desc->verf, sizeof(dir_ctx->verf));
|
||||
spin_unlock(&file->f_lock);
|
||||
|
||||
out_free:
|
||||
kfree(desc);
|
||||
|
||||
out:
|
||||
@ -1193,6 +1204,7 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int whence)
|
||||
if (offset == 0)
|
||||
memset(dir_ctx->verf, 0, sizeof(dir_ctx->verf));
|
||||
dir_ctx->duped = 0;
|
||||
dir_ctx->eof = false;
|
||||
}
|
||||
spin_unlock(&filp->f_lock);
|
||||
return offset;
|
||||
|
@ -8032,7 +8032,8 @@ static int _nfs41_proc_get_locations(struct nfs_server *server,
|
||||
|
||||
/**
|
||||
* nfs4_proc_get_locations - discover locations for a migrated FSID
|
||||
* @inode: inode on FSID that is migrating
|
||||
* @server: pointer to nfs_server to process
|
||||
* @fhandle: pointer to the kernel NFS client file handle
|
||||
* @locations: result of query
|
||||
* @page: buffer
|
||||
* @cred: credential to use for this operation
|
||||
|
@ -324,12 +324,12 @@ enum {
|
||||
ATA_LOG_NCQ_NON_DATA = 0x12,
|
||||
ATA_LOG_NCQ_SEND_RECV = 0x13,
|
||||
ATA_LOG_IDENTIFY_DEVICE = 0x30,
|
||||
ATA_LOG_CONCURRENT_POSITIONING_RANGES = 0x47,
|
||||
|
||||
/* Identify device log pages: */
|
||||
ATA_LOG_SECURITY = 0x06,
|
||||
ATA_LOG_SATA_SETTINGS = 0x08,
|
||||
ATA_LOG_ZONED_INFORMATION = 0x09,
|
||||
ATA_LOG_CONCURRENT_POSITIONING_RANGES = 0x47,
|
||||
|
||||
/* Identify device SATA settings log:*/
|
||||
ATA_LOG_DEVSLP_OFFSET = 0x30,
|
||||
|
@ -107,6 +107,7 @@ struct nfs_open_dir_context {
|
||||
__u64 dup_cookie;
|
||||
pgoff_t page_index;
|
||||
signed char duped;
|
||||
bool eof;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -562,9 +562,12 @@ struct kvm_s390_mem_op {
|
||||
__u32 op; /* type of operation */
|
||||
__u64 buf; /* buffer in userspace */
|
||||
union {
|
||||
__u8 ar; /* the access register number */
|
||||
struct {
|
||||
__u8 ar; /* the access register number */
|
||||
__u8 key; /* access key, ignored if flag unset */
|
||||
};
|
||||
__u32 sida_offset; /* offset into the sida */
|
||||
__u8 reserved[32]; /* should be set to 0 */
|
||||
__u8 reserved[32]; /* ignored */
|
||||
};
|
||||
};
|
||||
/* types for kvm_s390_mem_op->op */
|
||||
@ -572,9 +575,12 @@ struct kvm_s390_mem_op {
|
||||
#define KVM_S390_MEMOP_LOGICAL_WRITE 1
|
||||
#define KVM_S390_MEMOP_SIDA_READ 2
|
||||
#define KVM_S390_MEMOP_SIDA_WRITE 3
|
||||
#define KVM_S390_MEMOP_ABSOLUTE_READ 4
|
||||
#define KVM_S390_MEMOP_ABSOLUTE_WRITE 5
|
||||
/* flags for kvm_s390_mem_op->flags */
|
||||
#define KVM_S390_MEMOP_F_CHECK_ONLY (1ULL << 0)
|
||||
#define KVM_S390_MEMOP_F_INJECT_EXCEPTION (1ULL << 1)
|
||||
#define KVM_S390_MEMOP_F_SKEY_PROTECTION (1ULL << 2)
|
||||
|
||||
/* for KVM_INTERRUPT */
|
||||
struct kvm_interrupt {
|
||||
@ -1134,6 +1140,7 @@ struct kvm_ppc_resize_hpt {
|
||||
#define KVM_CAP_VM_GPA_BITS 207
|
||||
#define KVM_CAP_XSAVE2 208
|
||||
#define KVM_CAP_SYS_ATTRIBUTES 209
|
||||
#define KVM_CAP_S390_MEM_OP_EXTENSION 210
|
||||
|
||||
#ifdef KVM_CAP_IRQ_ROUTING
|
||||
|
||||
|
@ -115,11 +115,14 @@ static ssize_t rpc_sysfs_xprt_srcaddr_show(struct kobject *kobj,
|
||||
}
|
||||
|
||||
sock = container_of(xprt, struct sock_xprt, xprt);
|
||||
if (kernel_getsockname(sock->sock, (struct sockaddr *)&saddr) < 0)
|
||||
mutex_lock(&sock->recv_mutex);
|
||||
if (sock->sock == NULL ||
|
||||
kernel_getsockname(sock->sock, (struct sockaddr *)&saddr) < 0)
|
||||
goto out;
|
||||
|
||||
ret = sprintf(buf, "%pISc\n", &saddr);
|
||||
out:
|
||||
mutex_unlock(&sock->recv_mutex);
|
||||
xprt_put(xprt);
|
||||
return ret + 1;
|
||||
}
|
||||
|
@ -413,6 +413,7 @@ static int rpcrdma_ep_create(struct rpcrdma_xprt *r_xprt)
|
||||
IB_POLL_WORKQUEUE);
|
||||
if (IS_ERR(ep->re_attr.send_cq)) {
|
||||
rc = PTR_ERR(ep->re_attr.send_cq);
|
||||
ep->re_attr.send_cq = NULL;
|
||||
goto out_destroy;
|
||||
}
|
||||
|
||||
@ -421,6 +422,7 @@ static int rpcrdma_ep_create(struct rpcrdma_xprt *r_xprt)
|
||||
IB_POLL_WORKQUEUE);
|
||||
if (IS_ERR(ep->re_attr.recv_cq)) {
|
||||
rc = PTR_ERR(ep->re_attr.recv_cq);
|
||||
ep->re_attr.recv_cq = NULL;
|
||||
goto out_destroy;
|
||||
}
|
||||
ep->re_receive_count = 0;
|
||||
@ -459,6 +461,7 @@ static int rpcrdma_ep_create(struct rpcrdma_xprt *r_xprt)
|
||||
ep->re_pd = ib_alloc_pd(device, 0);
|
||||
if (IS_ERR(ep->re_pd)) {
|
||||
rc = PTR_ERR(ep->re_pd);
|
||||
ep->re_pd = NULL;
|
||||
goto out_destroy;
|
||||
}
|
||||
|
||||
|
@ -1641,7 +1641,12 @@ static int xs_get_srcport(struct sock_xprt *transport)
|
||||
unsigned short get_srcport(struct rpc_xprt *xprt)
|
||||
{
|
||||
struct sock_xprt *sock = container_of(xprt, struct sock_xprt, xprt);
|
||||
return xs_sock_getport(sock->sock);
|
||||
unsigned short ret = 0;
|
||||
mutex_lock(&sock->recv_mutex);
|
||||
if (sock->sock)
|
||||
ret = xs_sock_getport(sock->sock);
|
||||
mutex_unlock(&sock->recv_mutex);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(get_srcport);
|
||||
|
||||
|
@ -109,22 +109,25 @@ int asymmetric_verify(struct key *keyring, const char *sig,
|
||||
|
||||
pk = asymmetric_key_public_key(key);
|
||||
pks.pkey_algo = pk->pkey_algo;
|
||||
if (!strcmp(pk->pkey_algo, "rsa"))
|
||||
if (!strcmp(pk->pkey_algo, "rsa")) {
|
||||
pks.encoding = "pkcs1";
|
||||
else if (!strncmp(pk->pkey_algo, "ecdsa-", 6))
|
||||
} else if (!strncmp(pk->pkey_algo, "ecdsa-", 6)) {
|
||||
/* edcsa-nist-p192 etc. */
|
||||
pks.encoding = "x962";
|
||||
else if (!strcmp(pk->pkey_algo, "ecrdsa") ||
|
||||
!strcmp(pk->pkey_algo, "sm2"))
|
||||
} else if (!strcmp(pk->pkey_algo, "ecrdsa") ||
|
||||
!strcmp(pk->pkey_algo, "sm2")) {
|
||||
pks.encoding = "raw";
|
||||
else
|
||||
return -ENOPKG;
|
||||
} else {
|
||||
ret = -ENOPKG;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pks.digest = (u8 *)data;
|
||||
pks.digest_size = datalen;
|
||||
pks.s = hdr->sig;
|
||||
pks.s_size = siglen;
|
||||
ret = verify_signature(key, &pks);
|
||||
out:
|
||||
key_put(key);
|
||||
pr_debug("%s() = %d\n", __func__, ret);
|
||||
return ret;
|
||||
|
@ -496,12 +496,12 @@ int __init ima_fs_init(void)
|
||||
|
||||
return 0;
|
||||
out:
|
||||
securityfs_remove(ima_policy);
|
||||
securityfs_remove(violations);
|
||||
securityfs_remove(runtime_measurements_count);
|
||||
securityfs_remove(ascii_runtime_measurements);
|
||||
securityfs_remove(binary_runtime_measurements);
|
||||
securityfs_remove(ima_symlink);
|
||||
securityfs_remove(ima_dir);
|
||||
securityfs_remove(ima_policy);
|
||||
return -1;
|
||||
}
|
||||
|
@ -1967,6 +1967,14 @@ int ima_policy_show(struct seq_file *m, void *v)
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/* Do not print rules with inactive LSM labels */
|
||||
for (i = 0; i < MAX_LSM_RULES; i++) {
|
||||
if (entry->lsm[i].args_p && !entry->lsm[i].rule) {
|
||||
rcu_read_unlock();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->action & MEASURE)
|
||||
seq_puts(m, pt(Opt_measure));
|
||||
if (entry->action & DONT_MEASURE)
|
||||
|
@ -29,6 +29,7 @@ static struct ima_template_desc builtin_templates[] = {
|
||||
|
||||
static LIST_HEAD(defined_templates);
|
||||
static DEFINE_SPINLOCK(template_list);
|
||||
static int template_setup_done;
|
||||
|
||||
static const struct ima_template_field supported_fields[] = {
|
||||
{.field_id = "d", .field_init = ima_eventdigest_init,
|
||||
@ -101,10 +102,11 @@ static int __init ima_template_setup(char *str)
|
||||
struct ima_template_desc *template_desc;
|
||||
int template_len = strlen(str);
|
||||
|
||||
if (ima_template)
|
||||
if (template_setup_done)
|
||||
return 1;
|
||||
|
||||
ima_init_template_list();
|
||||
if (!ima_template)
|
||||
ima_init_template_list();
|
||||
|
||||
/*
|
||||
* Verify that a template with the supplied name exists.
|
||||
@ -128,6 +130,7 @@ static int __init ima_template_setup(char *str)
|
||||
}
|
||||
|
||||
ima_template = template_desc;
|
||||
template_setup_done = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_template=", ima_template_setup);
|
||||
@ -136,7 +139,7 @@ static int __init ima_template_fmt_setup(char *str)
|
||||
{
|
||||
int num_templates = ARRAY_SIZE(builtin_templates);
|
||||
|
||||
if (ima_template)
|
||||
if (template_setup_done)
|
||||
return 1;
|
||||
|
||||
if (template_desc_init_fields(str, NULL, NULL) < 0) {
|
||||
@ -147,6 +150,7 @@ static int __init ima_template_fmt_setup(char *str)
|
||||
|
||||
builtin_templates[num_templates - 1].fmt = str;
|
||||
ima_template = builtin_templates + num_templates - 1;
|
||||
template_setup_done = 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -45,6 +45,8 @@ void integrity_audit_message(int audit_msgno, struct inode *inode,
|
||||
return;
|
||||
|
||||
ab = audit_log_start(audit_context(), GFP_KERNEL, audit_msgno);
|
||||
if (!ab)
|
||||
return;
|
||||
audit_log_format(ab, "pid=%d uid=%u auid=%u ses=%u",
|
||||
task_pid_nr(current),
|
||||
from_kuid(&init_user_ns, current_uid()),
|
||||
|
1
tools/testing/selftests/kvm/.gitignore
vendored
1
tools/testing/selftests/kvm/.gitignore
vendored
@ -8,6 +8,7 @@
|
||||
/s390x/memop
|
||||
/s390x/resets
|
||||
/s390x/sync_regs_test
|
||||
/s390x/tprot
|
||||
/x86_64/amx_test
|
||||
/x86_64/cpuid_test
|
||||
/x86_64/cr4_cpuid_sync_test
|
||||
|
@ -121,6 +121,7 @@ TEST_GEN_PROGS_aarch64 += kvm_binary_stats_test
|
||||
TEST_GEN_PROGS_s390x = s390x/memop
|
||||
TEST_GEN_PROGS_s390x += s390x/resets
|
||||
TEST_GEN_PROGS_s390x += s390x/sync_regs_test
|
||||
TEST_GEN_PROGS_s390x += s390x/tprot
|
||||
TEST_GEN_PROGS_s390x += demand_paging_test
|
||||
TEST_GEN_PROGS_s390x += dirty_log_test
|
||||
TEST_GEN_PROGS_s390x += kvm_create_max_vcpus
|
||||
|
@ -160,6 +160,21 @@ int main(int argc, char *argv[])
|
||||
run->psw_mask &= ~(3UL << (63 - 17)); /* Disable AR mode */
|
||||
vcpu_run(vm, VCPU_ID); /* Run to sync new state */
|
||||
|
||||
/* Check that the SIDA calls are rejected for non-protected guests */
|
||||
ksmo.gaddr = 0;
|
||||
ksmo.flags = 0;
|
||||
ksmo.size = 8;
|
||||
ksmo.op = KVM_S390_MEMOP_SIDA_READ;
|
||||
ksmo.buf = (uintptr_t)mem1;
|
||||
ksmo.sida_offset = 0x1c0;
|
||||
rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo);
|
||||
TEST_ASSERT(rv == -1 && errno == EINVAL,
|
||||
"ioctl does not reject SIDA_READ in non-protected mode");
|
||||
ksmo.op = KVM_S390_MEMOP_SIDA_WRITE;
|
||||
rv = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_MEM_OP, &ksmo);
|
||||
TEST_ASSERT(rv == -1 && errno == EINVAL,
|
||||
"ioctl does not reject SIDA_WRITE in non-protected mode");
|
||||
|
||||
kvm_vm_free(vm);
|
||||
|
||||
return 0;
|
||||
|
227
tools/testing/selftests/kvm/s390x/tprot.c
Normal file
227
tools/testing/selftests/kvm/s390x/tprot.c
Normal file
@ -0,0 +1,227 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Test TEST PROTECTION emulation.
|
||||
*
|
||||
* Copyright IBM Corp. 2021
|
||||
*/
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include "test_util.h"
|
||||
#include "kvm_util.h"
|
||||
|
||||
#define PAGE_SHIFT 12
|
||||
#define PAGE_SIZE (1 << PAGE_SHIFT)
|
||||
#define CR0_FETCH_PROTECTION_OVERRIDE (1UL << (63 - 38))
|
||||
#define CR0_STORAGE_PROTECTION_OVERRIDE (1UL << (63 - 39))
|
||||
|
||||
#define VCPU_ID 1
|
||||
|
||||
static __aligned(PAGE_SIZE) uint8_t pages[2][PAGE_SIZE];
|
||||
static uint8_t *const page_store_prot = pages[0];
|
||||
static uint8_t *const page_fetch_prot = pages[1];
|
||||
|
||||
/* Nonzero return value indicates that address not mapped */
|
||||
static int set_storage_key(void *addr, uint8_t key)
|
||||
{
|
||||
int not_mapped = 0;
|
||||
|
||||
asm volatile (
|
||||
"lra %[addr], 0(0,%[addr])\n"
|
||||
" jz 0f\n"
|
||||
" llill %[not_mapped],1\n"
|
||||
" j 1f\n"
|
||||
"0: sske %[key], %[addr]\n"
|
||||
"1:"
|
||||
: [addr] "+&a" (addr), [not_mapped] "+r" (not_mapped)
|
||||
: [key] "r" (key)
|
||||
: "cc"
|
||||
);
|
||||
return -not_mapped;
|
||||
}
|
||||
|
||||
enum permission {
|
||||
READ_WRITE = 0,
|
||||
READ = 1,
|
||||
RW_PROTECTED = 2,
|
||||
TRANSL_UNAVAIL = 3,
|
||||
};
|
||||
|
||||
static enum permission test_protection(void *addr, uint8_t key)
|
||||
{
|
||||
uint64_t mask;
|
||||
|
||||
asm volatile (
|
||||
"tprot %[addr], 0(%[key])\n"
|
||||
" ipm %[mask]\n"
|
||||
: [mask] "=r" (mask)
|
||||
: [addr] "Q" (*(char *)addr),
|
||||
[key] "a" (key)
|
||||
: "cc"
|
||||
);
|
||||
|
||||
return (enum permission)(mask >> 28);
|
||||
}
|
||||
|
||||
enum stage {
|
||||
STAGE_END,
|
||||
STAGE_INIT_SIMPLE,
|
||||
TEST_SIMPLE,
|
||||
STAGE_INIT_FETCH_PROT_OVERRIDE,
|
||||
TEST_FETCH_PROT_OVERRIDE,
|
||||
TEST_STORAGE_PROT_OVERRIDE,
|
||||
};
|
||||
|
||||
struct test {
|
||||
enum stage stage;
|
||||
void *addr;
|
||||
uint8_t key;
|
||||
enum permission expected;
|
||||
} tests[] = {
|
||||
/*
|
||||
* We perform each test in the array by executing TEST PROTECTION on
|
||||
* the specified addr with the specified key and checking if the returned
|
||||
* permissions match the expected value.
|
||||
* Both guest and host cooperate to set up the required test conditions.
|
||||
* A central condition is that the page targeted by addr has to be DAT
|
||||
* protected in the host mappings, in order for KVM to emulate the
|
||||
* TEST PROTECTION instruction.
|
||||
* Since the page tables are shared, the host uses mprotect to achieve
|
||||
* this.
|
||||
*
|
||||
* Test resulting in RW_PROTECTED/TRANSL_UNAVAIL will be interpreted
|
||||
* by SIE, not KVM, but there is no harm in testing them also.
|
||||
* See Enhanced Suppression-on-Protection Facilities in the
|
||||
* Interpretive-Execution Mode
|
||||
*/
|
||||
/*
|
||||
* guest: set storage key of page_store_prot to 1
|
||||
* storage key of page_fetch_prot to 9 and enable
|
||||
* protection for it
|
||||
* STAGE_INIT_SIMPLE
|
||||
* host: write protect both via mprotect
|
||||
*/
|
||||
/* access key 0 matches any storage key -> RW */
|
||||
{ TEST_SIMPLE, page_store_prot, 0x00, READ_WRITE },
|
||||
/* access key matches storage key -> RW */
|
||||
{ TEST_SIMPLE, page_store_prot, 0x10, READ_WRITE },
|
||||
/* mismatched keys, but no fetch protection -> RO */
|
||||
{ TEST_SIMPLE, page_store_prot, 0x20, READ },
|
||||
/* access key 0 matches any storage key -> RW */
|
||||
{ TEST_SIMPLE, page_fetch_prot, 0x00, READ_WRITE },
|
||||
/* access key matches storage key -> RW */
|
||||
{ TEST_SIMPLE, page_fetch_prot, 0x90, READ_WRITE },
|
||||
/* mismatched keys, fetch protection -> inaccessible */
|
||||
{ TEST_SIMPLE, page_fetch_prot, 0x10, RW_PROTECTED },
|
||||
/* page 0 not mapped yet -> translation not available */
|
||||
{ TEST_SIMPLE, (void *)0x00, 0x10, TRANSL_UNAVAIL },
|
||||
/*
|
||||
* host: try to map page 0
|
||||
* guest: set storage key of page 0 to 9 and enable fetch protection
|
||||
* STAGE_INIT_FETCH_PROT_OVERRIDE
|
||||
* host: write protect page 0
|
||||
* enable fetch protection override
|
||||
*/
|
||||
/* mismatched keys, fetch protection, but override applies -> RO */
|
||||
{ TEST_FETCH_PROT_OVERRIDE, (void *)0x00, 0x10, READ },
|
||||
/* mismatched keys, fetch protection, override applies to 0-2048 only -> inaccessible */
|
||||
{ TEST_FETCH_PROT_OVERRIDE, (void *)2049, 0x10, RW_PROTECTED },
|
||||
/*
|
||||
* host: enable storage protection override
|
||||
*/
|
||||
/* mismatched keys, but override applies (storage key 9) -> RW */
|
||||
{ TEST_STORAGE_PROT_OVERRIDE, page_fetch_prot, 0x10, READ_WRITE },
|
||||
/* mismatched keys, no fetch protection, override doesn't apply -> RO */
|
||||
{ TEST_STORAGE_PROT_OVERRIDE, page_store_prot, 0x20, READ },
|
||||
/* mismatched keys, but override applies (storage key 9) -> RW */
|
||||
{ TEST_STORAGE_PROT_OVERRIDE, (void *)2049, 0x10, READ_WRITE },
|
||||
/* end marker */
|
||||
{ STAGE_END, 0, 0, 0 },
|
||||
};
|
||||
|
||||
static enum stage perform_next_stage(int *i, bool mapped_0)
|
||||
{
|
||||
enum stage stage = tests[*i].stage;
|
||||
enum permission result;
|
||||
bool skip;
|
||||
|
||||
for (; tests[*i].stage == stage; (*i)++) {
|
||||
/*
|
||||
* Some fetch protection override tests require that page 0
|
||||
* be mapped, however, when the hosts tries to map that page via
|
||||
* vm_vaddr_alloc, it may happen that some other page gets mapped
|
||||
* instead.
|
||||
* In order to skip these tests we detect this inside the guest
|
||||
*/
|
||||
skip = tests[*i].addr < (void *)4096 &&
|
||||
tests[*i].expected != TRANSL_UNAVAIL &&
|
||||
!mapped_0;
|
||||
if (!skip) {
|
||||
result = test_protection(tests[*i].addr, tests[*i].key);
|
||||
GUEST_ASSERT_2(result == tests[*i].expected, *i, result);
|
||||
}
|
||||
}
|
||||
return stage;
|
||||
}
|
||||
|
||||
static void guest_code(void)
|
||||
{
|
||||
bool mapped_0;
|
||||
int i = 0;
|
||||
|
||||
GUEST_ASSERT_EQ(set_storage_key(page_store_prot, 0x10), 0);
|
||||
GUEST_ASSERT_EQ(set_storage_key(page_fetch_prot, 0x98), 0);
|
||||
GUEST_SYNC(STAGE_INIT_SIMPLE);
|
||||
GUEST_SYNC(perform_next_stage(&i, false));
|
||||
|
||||
/* Fetch-protection override */
|
||||
mapped_0 = !set_storage_key((void *)0, 0x98);
|
||||
GUEST_SYNC(STAGE_INIT_FETCH_PROT_OVERRIDE);
|
||||
GUEST_SYNC(perform_next_stage(&i, mapped_0));
|
||||
|
||||
/* Storage-protection override */
|
||||
GUEST_SYNC(perform_next_stage(&i, mapped_0));
|
||||
}
|
||||
|
||||
#define HOST_SYNC(vmp, stage) \
|
||||
({ \
|
||||
struct kvm_vm *__vm = (vmp); \
|
||||
struct ucall uc; \
|
||||
int __stage = (stage); \
|
||||
\
|
||||
vcpu_run(__vm, VCPU_ID); \
|
||||
get_ucall(__vm, VCPU_ID, &uc); \
|
||||
if (uc.cmd == UCALL_ABORT) { \
|
||||
TEST_FAIL("line %lu: %s, hints: %lu, %lu", uc.args[1], \
|
||||
(const char *)uc.args[0], uc.args[2], uc.args[3]); \
|
||||
} \
|
||||
ASSERT_EQ(uc.cmd, UCALL_SYNC); \
|
||||
ASSERT_EQ(uc.args[1], __stage); \
|
||||
})
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct kvm_vm *vm;
|
||||
struct kvm_run *run;
|
||||
vm_vaddr_t guest_0_page;
|
||||
|
||||
vm = vm_create_default(VCPU_ID, 0, guest_code);
|
||||
run = vcpu_state(vm, VCPU_ID);
|
||||
|
||||
HOST_SYNC(vm, STAGE_INIT_SIMPLE);
|
||||
mprotect(addr_gva2hva(vm, (vm_vaddr_t)pages), PAGE_SIZE * 2, PROT_READ);
|
||||
HOST_SYNC(vm, TEST_SIMPLE);
|
||||
|
||||
guest_0_page = vm_vaddr_alloc(vm, PAGE_SIZE, 0);
|
||||
if (guest_0_page != 0)
|
||||
print_skip("Did not allocate page at 0 for fetch protection override tests");
|
||||
HOST_SYNC(vm, STAGE_INIT_FETCH_PROT_OVERRIDE);
|
||||
if (guest_0_page == 0)
|
||||
mprotect(addr_gva2hva(vm, (vm_vaddr_t)0), PAGE_SIZE, PROT_READ);
|
||||
run->s.regs.crs[0] |= CR0_FETCH_PROTECTION_OVERRIDE;
|
||||
run->kvm_dirty_regs = KVM_SYNC_CRS;
|
||||
HOST_SYNC(vm, TEST_FETCH_PROT_OVERRIDE);
|
||||
|
||||
run->s.regs.crs[0] |= CR0_STORAGE_PROTECTION_OVERRIDE;
|
||||
run->kvm_dirty_regs = KVM_SYNC_CRS;
|
||||
HOST_SYNC(vm, TEST_STORAGE_PROT_OVERRIDE);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user