forked from Minki/linux
[S390] Use gmap translation for accessing guest memory
This patch removes kvm-s390 internal assumption of a linear mapping of guest address space to user space. Previously, guest memory was translated to user addresses using a fixed offset (gmsor). The new code uses gmap_fault to resolve guest addresses. Signed-off-by: Carsten Otte <cotte@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
598841ca99
commit
092670cd90
@ -93,9 +93,7 @@ struct kvm_s390_sie_block {
|
||||
__u32 scaol; /* 0x0064 */
|
||||
__u8 reserved68[4]; /* 0x0068 */
|
||||
__u32 todpr; /* 0x006c */
|
||||
__u8 reserved70[16]; /* 0x0070 */
|
||||
__u64 gmsor; /* 0x0080 */
|
||||
__u64 gmslm; /* 0x0088 */
|
||||
__u8 reserved70[32]; /* 0x0070 */
|
||||
psw_t gpsw; /* 0x0090 */
|
||||
__u64 gg14; /* 0x00a0 */
|
||||
__u64 gg15; /* 0x00a8 */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* gaccess.h - access guest memory
|
||||
* access.h - access guest memory
|
||||
*
|
||||
* Copyright IBM Corp. 2008,2009
|
||||
*
|
||||
@ -22,20 +22,13 @@ static inline void __user *__guestaddr_to_user(struct kvm_vcpu *vcpu,
|
||||
unsigned long guestaddr)
|
||||
{
|
||||
unsigned long prefix = vcpu->arch.sie_block->prefix;
|
||||
unsigned long origin = vcpu->arch.sie_block->gmsor;
|
||||
unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
|
||||
|
||||
if (guestaddr < 2 * PAGE_SIZE)
|
||||
guestaddr += prefix;
|
||||
else if ((guestaddr >= prefix) && (guestaddr < prefix + 2 * PAGE_SIZE))
|
||||
guestaddr -= prefix;
|
||||
|
||||
if (guestaddr > memsize)
|
||||
return (void __user __force *) ERR_PTR(-EFAULT);
|
||||
|
||||
guestaddr += origin;
|
||||
|
||||
return (void __user *) guestaddr;
|
||||
return (void __user *) gmap_fault(guestaddr, vcpu->arch.gmap);
|
||||
}
|
||||
|
||||
static inline int get_guest_u64(struct kvm_vcpu *vcpu, unsigned long guestaddr,
|
||||
@ -141,11 +134,11 @@ static inline int put_guest_u8(struct kvm_vcpu *vcpu, unsigned long guestaddr,
|
||||
|
||||
static inline int __copy_to_guest_slow(struct kvm_vcpu *vcpu,
|
||||
unsigned long guestdest,
|
||||
const void *from, unsigned long n)
|
||||
void *from, unsigned long n)
|
||||
{
|
||||
int rc;
|
||||
unsigned long i;
|
||||
const u8 *data = from;
|
||||
u8 *data = from;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
rc = put_guest_u8(vcpu, guestdest++, *(data++));
|
||||
@ -155,12 +148,95 @@ static inline int __copy_to_guest_slow(struct kvm_vcpu *vcpu,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __copy_to_guest_fast(struct kvm_vcpu *vcpu,
|
||||
unsigned long guestdest,
|
||||
void *from, unsigned long n)
|
||||
{
|
||||
int r;
|
||||
void __user *uptr;
|
||||
unsigned long size;
|
||||
|
||||
if (guestdest + n < guestdest)
|
||||
return -EFAULT;
|
||||
|
||||
/* simple case: all within one segment table entry? */
|
||||
if ((guestdest & PMD_MASK) == ((guestdest+n) & PMD_MASK)) {
|
||||
uptr = (void __user *) gmap_fault(guestdest, vcpu->arch.gmap);
|
||||
|
||||
if (IS_ERR((void __force *) uptr))
|
||||
return PTR_ERR((void __force *) uptr);
|
||||
|
||||
r = copy_to_user(uptr, from, n);
|
||||
|
||||
if (r)
|
||||
r = -EFAULT;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* copy first segment */
|
||||
uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
|
||||
|
||||
if (IS_ERR((void __force *) uptr))
|
||||
return PTR_ERR((void __force *) uptr);
|
||||
|
||||
size = PMD_SIZE - (guestdest & ~PMD_MASK);
|
||||
|
||||
r = copy_to_user(uptr, from, size);
|
||||
|
||||
if (r) {
|
||||
r = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
from += size;
|
||||
n -= size;
|
||||
guestdest += size;
|
||||
|
||||
/* copy full segments */
|
||||
while (n >= PMD_SIZE) {
|
||||
uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
|
||||
|
||||
if (IS_ERR((void __force *) uptr))
|
||||
return PTR_ERR((void __force *) uptr);
|
||||
|
||||
r = copy_to_user(uptr, from, PMD_SIZE);
|
||||
|
||||
if (r) {
|
||||
r = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
from += PMD_SIZE;
|
||||
n -= PMD_SIZE;
|
||||
guestdest += PMD_SIZE;
|
||||
}
|
||||
|
||||
/* copy the tail segment */
|
||||
if (n) {
|
||||
uptr = (void __user *)gmap_fault(guestdest, vcpu->arch.gmap);
|
||||
|
||||
if (IS_ERR((void __force *) uptr))
|
||||
return PTR_ERR((void __force *) uptr);
|
||||
|
||||
r = copy_to_user(uptr, from, n);
|
||||
|
||||
if (r)
|
||||
r = -EFAULT;
|
||||
}
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline int copy_to_guest_absolute(struct kvm_vcpu *vcpu,
|
||||
unsigned long guestdest,
|
||||
void *from, unsigned long n)
|
||||
{
|
||||
return __copy_to_guest_fast(vcpu, guestdest, from, n);
|
||||
}
|
||||
|
||||
static inline int copy_to_guest(struct kvm_vcpu *vcpu, unsigned long guestdest,
|
||||
const void *from, unsigned long n)
|
||||
void *from, unsigned long n)
|
||||
{
|
||||
unsigned long prefix = vcpu->arch.sie_block->prefix;
|
||||
unsigned long origin = vcpu->arch.sie_block->gmsor;
|
||||
unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
|
||||
|
||||
if ((guestdest < 2 * PAGE_SIZE) && (guestdest + n > 2 * PAGE_SIZE))
|
||||
goto slowpath;
|
||||
@ -177,15 +253,7 @@ static inline int copy_to_guest(struct kvm_vcpu *vcpu, unsigned long guestdest,
|
||||
else if ((guestdest >= prefix) && (guestdest < prefix + 2 * PAGE_SIZE))
|
||||
guestdest -= prefix;
|
||||
|
||||
if (guestdest + n > memsize)
|
||||
return -EFAULT;
|
||||
|
||||
if (guestdest + n < guestdest)
|
||||
return -EFAULT;
|
||||
|
||||
guestdest += origin;
|
||||
|
||||
return copy_to_user((void __user *) guestdest, from, n);
|
||||
return __copy_to_guest_fast(vcpu, guestdest, from, n);
|
||||
slowpath:
|
||||
return __copy_to_guest_slow(vcpu, guestdest, from, n);
|
||||
}
|
||||
@ -206,12 +274,95 @@ static inline int __copy_from_guest_slow(struct kvm_vcpu *vcpu, void *to,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __copy_from_guest_fast(struct kvm_vcpu *vcpu, void *to,
|
||||
unsigned long guestsrc,
|
||||
unsigned long n)
|
||||
{
|
||||
int r;
|
||||
void __user *uptr;
|
||||
unsigned long size;
|
||||
|
||||
if (guestsrc + n < guestsrc)
|
||||
return -EFAULT;
|
||||
|
||||
/* simple case: all within one segment table entry? */
|
||||
if ((guestsrc & PMD_MASK) == ((guestsrc+n) & PMD_MASK)) {
|
||||
uptr = (void __user *) gmap_fault(guestsrc, vcpu->arch.gmap);
|
||||
|
||||
if (IS_ERR((void __force *) uptr))
|
||||
return PTR_ERR((void __force *) uptr);
|
||||
|
||||
r = copy_from_user(to, uptr, n);
|
||||
|
||||
if (r)
|
||||
r = -EFAULT;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* copy first segment */
|
||||
uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
|
||||
|
||||
if (IS_ERR((void __force *) uptr))
|
||||
return PTR_ERR((void __force *) uptr);
|
||||
|
||||
size = PMD_SIZE - (guestsrc & ~PMD_MASK);
|
||||
|
||||
r = copy_from_user(to, uptr, size);
|
||||
|
||||
if (r) {
|
||||
r = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
to += size;
|
||||
n -= size;
|
||||
guestsrc += size;
|
||||
|
||||
/* copy full segments */
|
||||
while (n >= PMD_SIZE) {
|
||||
uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
|
||||
|
||||
if (IS_ERR((void __force *) uptr))
|
||||
return PTR_ERR((void __force *) uptr);
|
||||
|
||||
r = copy_from_user(to, uptr, PMD_SIZE);
|
||||
|
||||
if (r) {
|
||||
r = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
to += PMD_SIZE;
|
||||
n -= PMD_SIZE;
|
||||
guestsrc += PMD_SIZE;
|
||||
}
|
||||
|
||||
/* copy the tail segment */
|
||||
if (n) {
|
||||
uptr = (void __user *)gmap_fault(guestsrc, vcpu->arch.gmap);
|
||||
|
||||
if (IS_ERR((void __force *) uptr))
|
||||
return PTR_ERR((void __force *) uptr);
|
||||
|
||||
r = copy_from_user(to, uptr, n);
|
||||
|
||||
if (r)
|
||||
r = -EFAULT;
|
||||
}
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline int copy_from_guest_absolute(struct kvm_vcpu *vcpu, void *to,
|
||||
unsigned long guestsrc,
|
||||
unsigned long n)
|
||||
{
|
||||
return __copy_from_guest_fast(vcpu, to, guestsrc, n);
|
||||
}
|
||||
|
||||
static inline int copy_from_guest(struct kvm_vcpu *vcpu, void *to,
|
||||
unsigned long guestsrc, unsigned long n)
|
||||
{
|
||||
unsigned long prefix = vcpu->arch.sie_block->prefix;
|
||||
unsigned long origin = vcpu->arch.sie_block->gmsor;
|
||||
unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
|
||||
|
||||
if ((guestsrc < 2 * PAGE_SIZE) && (guestsrc + n > 2 * PAGE_SIZE))
|
||||
goto slowpath;
|
||||
@ -228,52 +379,8 @@ static inline int copy_from_guest(struct kvm_vcpu *vcpu, void *to,
|
||||
else if ((guestsrc >= prefix) && (guestsrc < prefix + 2 * PAGE_SIZE))
|
||||
guestsrc -= prefix;
|
||||
|
||||
if (guestsrc + n > memsize)
|
||||
return -EFAULT;
|
||||
|
||||
if (guestsrc + n < guestsrc)
|
||||
return -EFAULT;
|
||||
|
||||
guestsrc += origin;
|
||||
|
||||
return copy_from_user(to, (void __user *) guestsrc, n);
|
||||
return __copy_from_guest_fast(vcpu, to, guestsrc, n);
|
||||
slowpath:
|
||||
return __copy_from_guest_slow(vcpu, to, guestsrc, n);
|
||||
}
|
||||
|
||||
static inline int copy_to_guest_absolute(struct kvm_vcpu *vcpu,
|
||||
unsigned long guestdest,
|
||||
const void *from, unsigned long n)
|
||||
{
|
||||
unsigned long origin = vcpu->arch.sie_block->gmsor;
|
||||
unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
|
||||
|
||||
if (guestdest + n > memsize)
|
||||
return -EFAULT;
|
||||
|
||||
if (guestdest + n < guestdest)
|
||||
return -EFAULT;
|
||||
|
||||
guestdest += origin;
|
||||
|
||||
return copy_to_user((void __user *) guestdest, from, n);
|
||||
}
|
||||
|
||||
static inline int copy_from_guest_absolute(struct kvm_vcpu *vcpu, void *to,
|
||||
unsigned long guestsrc,
|
||||
unsigned long n)
|
||||
{
|
||||
unsigned long origin = vcpu->arch.sie_block->gmsor;
|
||||
unsigned long memsize = kvm_s390_vcpu_get_memsize(vcpu);
|
||||
|
||||
if (guestsrc + n > memsize)
|
||||
return -EFAULT;
|
||||
|
||||
if (guestsrc + n < guestsrc)
|
||||
return -EFAULT;
|
||||
|
||||
guestsrc += origin;
|
||||
|
||||
return copy_from_user(to, (void __user *) guestsrc, n);
|
||||
}
|
||||
#endif
|
||||
|
@ -165,29 +165,33 @@ static int handle_validity(struct kvm_vcpu *vcpu)
|
||||
int rc;
|
||||
|
||||
vcpu->stat.exit_validity++;
|
||||
if ((viwhy == 0x37) && (vcpu->arch.sie_block->prefix
|
||||
<= kvm_s390_vcpu_get_memsize(vcpu) - 2*PAGE_SIZE)) {
|
||||
rc = fault_in_pages_writeable((char __user *)
|
||||
vcpu->arch.sie_block->gmsor +
|
||||
vcpu->arch.sie_block->prefix,
|
||||
2*PAGE_SIZE);
|
||||
if (rc) {
|
||||
/* user will receive sigsegv, exit to user */
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
if (viwhy == 0x37) {
|
||||
vmaddr = gmap_fault(vcpu->arch.sie_block->prefix,
|
||||
vcpu->arch.gmap);
|
||||
if (IS_ERR_VALUE(vmaddr)) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
rc = fault_in_pages_writeable((char __user *) vmaddr,
|
||||
PAGE_SIZE);
|
||||
if (rc) {
|
||||
/* user will receive sigsegv, exit to user */
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
vmaddr = gmap_fault(vcpu->arch.sie_block->prefix + PAGE_SIZE,
|
||||
vcpu->arch.gmap);
|
||||
if (IS_ERR_VALUE(vmaddr)) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
rc = fault_in_pages_writeable((char __user *) vmaddr,
|
||||
PAGE_SIZE);
|
||||
if (rc) {
|
||||
/* user will receive sigsegv, exit to user */
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
} else
|
||||
rc = -EOPNOTSUPP;
|
||||
|
||||
|
@ -549,7 +549,7 @@ rerun_vcpu:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __guestcopy(struct kvm_vcpu *vcpu, u64 guestdest, const void *from,
|
||||
static int __guestcopy(struct kvm_vcpu *vcpu, u64 guestdest, void *from,
|
||||
unsigned long n, int prefix)
|
||||
{
|
||||
if (prefix)
|
||||
@ -566,7 +566,7 @@ static int __guestcopy(struct kvm_vcpu *vcpu, u64 guestdest, const void *from,
|
||||
*/
|
||||
int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
|
||||
{
|
||||
const unsigned char archmode = 1;
|
||||
unsigned char archmode = 1;
|
||||
int prefix;
|
||||
|
||||
if (addr == KVM_S390_STORE_STATUS_NOADDR) {
|
||||
|
@ -58,31 +58,8 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
|
||||
int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code);
|
||||
int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action);
|
||||
|
||||
static inline long kvm_s390_vcpu_get_memsize(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
return vcpu->arch.sie_block->gmslm
|
||||
- vcpu->arch.sie_block->gmsor
|
||||
- VIRTIODESCSPACE + 1ul;
|
||||
}
|
||||
|
||||
static inline void kvm_s390_vcpu_set_mem(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
int idx;
|
||||
struct kvm_memory_slot *mem;
|
||||
struct kvm_memslots *memslots;
|
||||
|
||||
idx = srcu_read_lock(&vcpu->kvm->srcu);
|
||||
memslots = kvm_memslots(vcpu->kvm);
|
||||
|
||||
mem = &memslots->memslots[0];
|
||||
|
||||
vcpu->arch.sie_block->gmsor = mem->userspace_addr;
|
||||
vcpu->arch.sie_block->gmslm =
|
||||
mem->userspace_addr +
|
||||
(mem->npages << PAGE_SHIFT) +
|
||||
VIRTIODESCSPACE - 1ul;
|
||||
|
||||
srcu_read_unlock(&vcpu->kvm->srcu, idx);
|
||||
}
|
||||
|
||||
/* implemented in priv.c */
|
||||
|
@ -189,10 +189,8 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address,
|
||||
|
||||
/* make sure that the new value is valid memory */
|
||||
address = address & 0x7fffe000u;
|
||||
if ((copy_from_user(&tmp, (void __user *)
|
||||
(address + vcpu->arch.sie_block->gmsor) , 1)) ||
|
||||
(copy_from_user(&tmp, (void __user *)(address +
|
||||
vcpu->arch.sie_block->gmsor + PAGE_SIZE), 1))) {
|
||||
if (copy_from_guest_absolute(vcpu, &tmp, address, 1) ||
|
||||
copy_from_guest_absolute(vcpu, &tmp, address + PAGE_SIZE, 1)) {
|
||||
*reg |= SIGP_STAT_INVALID_PARAMETER;
|
||||
return 1; /* invalid parameter */
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user