@@ -523,6 +523,10 @@ int kvm_arm_pvtime_get_attr(struct kvm_vcpu *vcpu,
int kvm_arm_pvtime_has_attr(struct kvm_vcpu *vcpu,
struct kvm_device_attr *attr);
+int kvm_arm_pvtime_lpt_set_attr(struct kvm *kvm, struct kvm_device_attr *attr);
+int kvm_arm_pvtime_lpt_get_attr(struct kvm *kvm, struct kvm_device_attr *attr);
+int kvm_arm_pvtime_lpt_has_attr(struct kvm *kvm, struct kvm_device_attr *attr);
+
static inline void kvm_arm_pvtime_vcpu_init(struct kvm_vcpu_arch *vcpu_arch)
{
vcpu_arch->steal.base = GPA_INVALID;
@@ -325,6 +325,11 @@ struct kvm_vcpu_events {
#define KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES 3
#define KVM_DEV_ARM_ITS_CTRL_RESET 4
+/* Device Control API on kvm fd */
+#define KVM_ARM_VM_PVTIME_LPT_CTRL 0
+#define KVM_ARM_VM_PVTIME_LPT_IPA 0
+#define KVM_ARM_VM_PVTIME_LPT_FREQ 1
+
/* Device Control API on vcpu fd */
#define KVM_ARM_VCPU_PMU_V3_CTRL 0
#define KVM_ARM_VCPU_PMU_V3_IRQ 0
@@ -1235,11 +1235,60 @@ static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm,
}
}
+static int kvm_arm_vm_set_attr(struct kvm *kvm, struct kvm_device_attr *attr)
+{
+ int ret;
+
+ switch (attr->group) {
+ case KVM_ARM_VM_PVTIME_LPT_CTRL:
+ ret = kvm_arm_pvtime_lpt_set_attr(kvm, attr);
+ break;
+ default:
+ ret = -ENXIO;
+ break;
+ }
+
+ return ret;
+}
+
+static int kvm_arm_vm_get_attr(struct kvm *kvm, struct kvm_device_attr *attr)
+{
+ int ret;
+
+ switch (attr->group) {
+ case KVM_ARM_VM_PVTIME_LPT_CTRL:
+ ret = kvm_arm_pvtime_lpt_get_attr(kvm, attr);
+ break;
+ default:
+ ret = -ENXIO;
+ break;
+ }
+
+ return ret;
+}
+
+static int kvm_arm_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
+{
+ int ret;
+
+ switch (attr->group) {
+ case KVM_ARM_VM_PVTIME_LPT_CTRL:
+ ret = kvm_arm_pvtime_lpt_has_attr(kvm, attr);
+ break;
+ default:
+ ret = -ENXIO;
+ break;
+ }
+
+ return ret;
+}
+
long kvm_arch_vm_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
struct kvm *kvm = filp->private_data;
void __user *argp = (void __user *)arg;
+ struct kvm_device_attr attr;
switch (ioctl) {
case KVM_CREATE_IRQCHIP: {
@@ -1271,6 +1320,21 @@ long kvm_arch_vm_ioctl(struct file *filp,
return 0;
}
+ case KVM_SET_DEVICE_ATTR: {
+ if (copy_from_user(&attr, argp, sizeof(attr)))
+ return -EFAULT;
+ return kvm_arm_vm_set_attr(kvm, &attr);
+ }
+ case KVM_GET_DEVICE_ATTR: {
+ if (copy_from_user(&attr, argp, sizeof(attr)))
+ return -EFAULT;
+ return kvm_arm_vm_get_attr(kvm, &attr);
+ }
+ case KVM_HAS_DEVICE_ATTR: {
+ if (copy_from_user(&attr, argp, sizeof(attr)))
+ return -EFAULT;
+ return kvm_arm_vm_has_attr(kvm, &attr);
+ }
default:
return -EINVAL;
}
@@ -257,3 +257,90 @@ gpa_t kvm_init_lpt_time(struct kvm *kvm)
{
return kvm->arch.lpt.base;
}
+
+int kvm_arm_pvtime_lpt_set_attr(struct kvm *kvm, struct kvm_device_attr *attr)
+{
+ u64 __user *user = (u64 __user *)attr->addr;
+ u64 ipa;
+ u32 freq;
+ int idx;
+ int ret = 0;
+
+ switch (attr->attr) {
+ case KVM_ARM_VM_PVTIME_LPT_IPA:
+ if (get_user(ipa, user)) {
+ ret = -EFAULT;
+ break;
+ }
+ if (!IS_ALIGNED(ipa, 64)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (kvm->arch.lpt.base != GPA_INVALID) {
+ ret = -EEXIST;
+ break;
+ }
+ /* Check the address is in a valid memslot */
+ idx = srcu_read_lock(&kvm->srcu);
+ if (kvm_is_error_hva(gfn_to_hva(kvm, ipa >> PAGE_SHIFT)))
+ ret = -EINVAL;
+ srcu_read_unlock(&kvm->srcu, idx);
+ if (ret)
+ break;
+
+ kvm->arch.lpt.base = ipa;
+ break;
+ case KVM_ARM_VM_PVTIME_LPT_FREQ:
+ if (get_user(freq, user)) {
+ ret = -EFAULT;
+ break;
+ }
+ if (freq == 0) {
+ ret = -EINVAL;
+ break;
+ }
+ if (kvm->arch.lpt.fpv != 0) {
+ ret = -EEXIST;
+ break;
+ }
+ kvm->arch.lpt.fpv = freq;
+ break;
+ default:
+ ret = -ENXIO;
+ break;
+ }
+
+ return ret;
+}
+
+int kvm_arm_pvtime_lpt_get_attr(struct kvm *kvm, struct kvm_device_attr *attr)
+{
+ u64 __user *user = (u64 __user *)attr->addr;
+ int ret = 0;
+
+ switch (attr->attr) {
+ case KVM_ARM_VM_PVTIME_LPT_IPA:
+ if (put_user(kvm->arch.lpt.base, user))
+ ret = -EFAULT;
+ break;
+ case KVM_ARM_VM_PVTIME_LPT_FREQ:
+ if (put_user(kvm->arch.lpt.fpv, user))
+ ret = -EFAULT;
+ break;
+ default:
+ ret = -ENXIO;
+ break;
+ }
+
+ return ret;
+}
+
+int kvm_arm_pvtime_lpt_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
+{
+ switch (attr->attr) {
+ case KVM_ARM_VM_PVTIME_LPT_IPA:
+ case KVM_ARM_VM_PVTIME_LPT_FREQ:
+ return 0;
+ }
+ return -ENXIO;
+}