From: Frank Blaschka
This patch implemets PCI pass-through kernel support for s390.
Design approach is very similar to the x86 device assignment.
User space executes the KVM_ASSIGN_PCI_DEVICE ioctl to create
a proxy instance in the kernel KVM and connect this instance to the
host pci device. s390 pci instructions are intercepted in kernel and
operations are passed directly to the assigned pci device.
To take advantage of all system z specific virtualization features
we need to access the SIE control block residing in KVM. Also we have to
enable z pci devices with special configuration information coming
form the SIE block as well.
Signed-off-by: Frank Blaschka
---
arch/s390/include/asm/kvm_host.h |1
arch/s390/kvm/Makefile |2
arch/s390/kvm/intercept.c|1
arch/s390/kvm/kvm-s390.c | 33
arch/s390/kvm/kvm-s390.h | 17
arch/s390/kvm/pci.c | 2130 +++
arch/s390/kvm/priv.c | 21
7 files changed, 2202 insertions(+), 3 deletions(-)
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -488,6 +488,7 @@ struct kvm_arch{
union kvm_s390_gisa *gisa;
unsigned long iam;
atomic_t in_sie;
+ struct list_head ppt_dev_list;
};
#define KVM_HVA_ERR_BAD(-1UL)
--- a/arch/s390/kvm/Makefile
+++ b/arch/s390/kvm/Makefile
@@ -12,6 +12,6 @@ common-objs = $(KVM)/kvm_main.o $(KVM)/e
ccflags-y := -Ivirt/kvm -Iarch/s390/kvm
kvm-objs := $(common-objs) kvm-s390.o intercept.o interrupt.o priv.o sigp.o
-kvm-objs += diag.o gaccess.o guestdbg.o
+kvm-objs += diag.o gaccess.o guestdbg.o pci.o
obj-$(CONFIG_KVM) += kvm.o
--- a/arch/s390/kvm/intercept.c
+++ b/arch/s390/kvm/intercept.c
@@ -34,6 +34,7 @@ static const intercept_handler_t instruc
[0xb6] = kvm_s390_handle_stctl,
[0xb7] = kvm_s390_handle_lctl,
[0xb9] = kvm_s390_handle_b9,
+ [0xe3] = kvm_s390_handle_e3,
[0xe5] = kvm_s390_handle_e5,
[0xeb] = kvm_s390_handle_eb,
};
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -397,6 +397,24 @@ long kvm_arch_vm_ioctl(struct file *filp
r = kvm_s390_vm_has_attr(kvm, &attr);
break;
}
+ case KVM_ASSIGN_PCI_DEVICE: {
+ struct kvm_assigned_pci_dev assigned_dev;
+
+ r = -EFAULT;
+ if (copy_from_user(&assigned_dev, argp, sizeof(assigned_dev)))
+ break;
+ r = kvm_s390_ioctrl_assign_pci(kvm, &assigned_dev);
+ break;
+ }
+ case KVM_DEASSIGN_PCI_DEVICE: {
+ struct kvm_assigned_pci_dev assigned_dev;
+
+ r = -EFAULT;
+ if (copy_from_user(&assigned_dev, argp, sizeof(assigned_dev)))
+ break;
+ r = kvm_s390_ioctrl_deassign_pci(kvm, &assigned_dev);
+ break;
+ }
default:
r = -ENOTTY;
}
@@ -478,6 +496,7 @@ int kvm_arch_init_vm(struct kvm *kvm, un
kvm_s390_gisa_set_next_alert(kvm, (u32)(unsigned long)kvm->arch.gisa);
kvm_s390_gisa_set_alert_mask(kvm, 0);
atomic_set(&kvm->arch.in_sie, 0);
+ INIT_LIST_HEAD(&kvm->arch.ppt_dev_list);
spin_lock_init(&kvm->arch.start_stop_lock);
@@ -538,6 +557,7 @@ void kvm_arch_sync_events(struct kvm *kv
void kvm_arch_destroy_vm(struct kvm *kvm)
{
+ s390_pci_cleanup(kvm);
free_page((unsigned long)kvm->arch.gisa);
kvm_free_vcpus(kvm);
free_page((unsigned long)(kvm->arch.sca));
@@ -656,7 +676,10 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu
vcpu->arch.sie_block->ecb |= 0x10;
vcpu->arch.sie_block->ecb2 = 8;
- vcpu->arch.sie_block->eca = 0xD1002000U;
+ vcpu->arch.sie_block->eca = 0xD1202000U;
+ vcpu->arch.sie_block->ecb2 |= 0x02;
+ vcpu->arch.sie_block->ecb3 = 0x20;
+
if (sclp_has_siif())
vcpu->arch.sie_block->eca |= 1;
vcpu->arch.sie_block->fac = (int) (long) vfacilities;
@@ -1920,6 +1943,12 @@ static int __init kvm_s390_init(void)
if (ret)
return ret;
+ ret = s390_pci_init();
+ if (ret) {
+ kvm_exit();
+ return ret;
+ }
+
/*
* guests can ask for up to 255+1 double words, we need a full page
* to hold the maximum amount of facilities. On the other hand, we
@@ -1932,7 +1961,7 @@ static int __init kvm_s390_init(void)
}
memcpy(vfacilities, S390_lowcore.stfle_fac_list, 16);
vfacilities[0] &= 0xff82fff3f4fc2000UL;
- vfacilities[1] &= 0x005cUL;
+ vfacilities[1] &= 0x07dcUL;
return 0;
}
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -167,6 +167,7 @@ int kvm_s390_mask_adapter(struct kvm *kv
/* implemented in priv.c */
int is_valid_psw(psw_t *psw);
int kvm_s390_handle_b2(s