@@ -310,8 +310,10 @@ struct kvm_vcpu_events {
/* Device Control API: PV_TIME */
#define KVM_DEV_ARM_PV_TIME_PADDR 0
#define KVM_DEV_ARM_PV_TIME_ST 0
+#define KVM_DEV_ARM_PV_TIME_LPT 1
#define KVM_DEV_ARM_PV_TIME_STATE_SIZE 1
#define KVM_DEV_ARM_PV_TIME_STATE 2
+#define KVM_DEV_ARM_PV_TIME_FREQUENCY 3
#endif
@@ -25,6 +25,14 @@ static int kvm_arm_pvtime_create(struct kvm_device *dev, u32 type)
if (!pvtime->st)
return -ENOMEM;
+ pvtime->lpt = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!pvtime->lpt) {
+ free_pages_exact(pvtime->st, max_stolen_size());
+ return -ENOMEM;
+ }
+
+ pvtime->lpt_fpv = arch_timer_get_rate();
+
return 0;
}
@@ -32,8 +40,10 @@ static void kvm_arm_pvtime_destroy(struct kvm_device *dev)
{
struct kvm_arch_pvtime *pvtime = &dev->kvm->arch.pvtime;
+ pvtime->lpt_base = GPA_INVALID;
pvtime->st_base = GPA_INVALID;
free_pages_exact(pvtime->st, max_stolen_size());
+ free_page((unsigned long)pvtime->lpt);
}
static int pvtime_map_pages(struct kvm *kvm, gpa_t guest_paddr,
@@ -50,6 +60,10 @@ static int pvtime_save_state(struct kvm *kvm, u64 type, void __user *user)
size_t size;
switch (type) {
+ case KVM_DEV_ARM_PV_TIME_LPT:
+ source = kvm->arch.pvtime.lpt;
+ size = sizeof(struct pvclock_vm_time_info);
+ break;
case KVM_DEV_ARM_PV_TIME_ST:
source = kvm->arch.pvtime.st;
size = sizeof(struct pvclock_vcpu_stolen_time_info) *
@@ -70,6 +84,10 @@ static int pvtime_restore_state(struct kvm *kvm, u64 type, void __user *user)
size_t size;
switch (type) {
+ case KVM_DEV_ARM_PV_TIME_LPT:
+ dest = kvm->arch.pvtime.lpt;
+ size = sizeof(struct pvclock_vm_time_info);
+ break;
case KVM_DEV_ARM_PV_TIME_ST:
dest = kvm->arch.pvtime.st;
size = sizeof(struct pvclock_vcpu_stolen_time_info) *
@@ -91,6 +109,7 @@ static int kvm_arm_pvtime_set_attr(struct kvm_device *dev,
struct kvm_arch_pvtime *pvtime = &dev->kvm->arch.pvtime;
u64 __user *user = (u64 __user *)attr->addr;
u64 paddr;
+ u32 frequency;
int ret;
switch (attr->group) {
@@ -100,6 +119,15 @@ static int kvm_arm_pvtime_set_attr(struct kvm_device *dev,
if (paddr & 63)
return -EINVAL;
switch (attr->attr) {
+ case KVM_DEV_ARM_PV_TIME_LPT:
+ if (pvtime->lpt_base != GPA_INVALID)
+ return -EEXIST;
+ ret = pvtime_map_pages(dev->kvm, paddr, pvtime->lpt,
+ PAGE_SIZE);
+ if (ret)
+ return ret;
+ pvtime->lpt_base = paddr;
+ return kvm_arm_update_lpt_sequence(dev->kvm);
case KVM_DEV_ARM_PV_TIME_ST:
if (pvtime->st_base != GPA_INVALID)
return -EEXIST;
@@ -111,6 +139,13 @@ static int kvm_arm_pvtime_set_attr(struct kvm_device *dev,
return 0;
}
break;
+ case KVM_DEV_ARM_PV_TIME_FREQUENCY:
+ if (attr->attr != KVM_DEV_ARM_PV_TIME_LPT)
+ break;
+ if (get_user(frequency, user))
+ return -EFAULT;
+ pvtime->lpt_fpv = frequency;
+ return kvm_arm_update_lpt_sequence(dev->kvm);
case KVM_DEV_ARM_PV_TIME_STATE_SIZE:
return -EPERM;
case KVM_DEV_ARM_PV_TIME_STATE:
@@ -128,14 +163,27 @@ static int kvm_arm_pvtime_get_attr(struct kvm_device *dev,
switch (attr->group) {
case KVM_DEV_ARM_PV_TIME_PADDR:
switch (attr->attr) {
+ case KVM_DEV_ARM_PV_TIME_LPT:
+ if (put_user(dev->kvm->arch.pvtime.lpt_base, user))
+ return -EFAULT;
+ return 0;
case KVM_DEV_ARM_PV_TIME_ST:
if (put_user(dev->kvm->arch.pvtime.st_base, user))
return -EFAULT;
return 0;
}
break;
+ case KVM_DEV_ARM_PV_TIME_FREQUENCY:
+ if (attr->attr != KVM_DEV_ARM_PV_TIME_LPT)
+ break;
+ if (put_user(dev->kvm->arch.pvtime.lpt_fpv, user))
+ return -EFAULT;
+ return 0;
case KVM_DEV_ARM_PV_TIME_STATE_SIZE:
switch (attr->attr) {
+ case KVM_DEV_ARM_PV_TIME_LPT:
+ size = sizeof(struct pvclock_vm_time_info);
+ break;
case KVM_DEV_ARM_PV_TIME_ST:
size = sizeof(struct pvclock_vcpu_stolen_time_info);
size *= atomic_read(&dev->kvm->online_vcpus);
@@ -160,10 +208,17 @@ static int kvm_arm_pvtime_has_attr(struct kvm_device *dev,
case KVM_DEV_ARM_PV_TIME_STATE_SIZE:
case KVM_DEV_ARM_PV_TIME_STATE:
switch (attr->attr) {
+ case KVM_DEV_ARM_PV_TIME_LPT:
case KVM_DEV_ARM_PV_TIME_ST:
return 0;
}
break;
+ case KVM_DEV_ARM_PV_TIME_FREQUENCY:
+ switch (attr->attr) {
+ case KVM_DEV_ARM_PV_TIME_LPT:
+ return 0;
+ }
+ break;
}
return -ENXIO;
}
Extend the PV_TIME device to allow setting up Live Physical Time and saving/restoring the state necessary for migration. Signed-off-by: Steven Price <steven.price@arm.com> --- arch/arm64/include/uapi/asm/kvm.h | 2 ++ virt/kvm/arm/pvtime.c | 55 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+)