mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-25 13:14:07 +08:00
RISC-V: KVM: Expose IMSIC registers as attributes of AIA irqchip
We expose IMSIC registers as KVM device attributes of the in-kernel AIA irqchip device. This will allow KVM user-space to save/restore IMISC state of each VCPU using KVM device ioctls(). Signed-off-by: Anup Patel <apatel@ventanamicro.com> Reviewed-by: Atish Patra <atishp@rivosinc.com> Signed-off-by: Anup Patel <anup@brainfault.org>
This commit is contained in:
parent
db8b7e97d6
commit
5463091a51
@ -97,6 +97,9 @@ int kvm_riscv_vcpu_aia_imsic_update(struct kvm_vcpu *vcpu);
|
|||||||
int kvm_riscv_vcpu_aia_imsic_rmw(struct kvm_vcpu *vcpu, unsigned long isel,
|
int kvm_riscv_vcpu_aia_imsic_rmw(struct kvm_vcpu *vcpu, unsigned long isel,
|
||||||
unsigned long *val, unsigned long new_val,
|
unsigned long *val, unsigned long new_val,
|
||||||
unsigned long wr_mask);
|
unsigned long wr_mask);
|
||||||
|
int kvm_riscv_aia_imsic_rw_attr(struct kvm *kvm, unsigned long type,
|
||||||
|
bool write, unsigned long *val);
|
||||||
|
int kvm_riscv_aia_imsic_has_attr(struct kvm *kvm, unsigned long type);
|
||||||
void kvm_riscv_vcpu_aia_imsic_reset(struct kvm_vcpu *vcpu);
|
void kvm_riscv_vcpu_aia_imsic_reset(struct kvm_vcpu *vcpu);
|
||||||
int kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu,
|
int kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu,
|
||||||
u32 guest_index, u32 offset, u32 iid);
|
u32 guest_index, u32 offset, u32 iid);
|
||||||
|
@ -255,6 +255,23 @@ enum KVM_RISCV_SBI_EXT_ID {
|
|||||||
*/
|
*/
|
||||||
#define KVM_DEV_RISCV_AIA_GRP_APLIC 3
|
#define KVM_DEV_RISCV_AIA_GRP_APLIC 3
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The lower 12-bits of the device attribute type contains the iselect
|
||||||
|
* value of the IMSIC register (range 0x70-0xFF) whereas the higher order
|
||||||
|
* bits contains the VCPU id.
|
||||||
|
*/
|
||||||
|
#define KVM_DEV_RISCV_AIA_GRP_IMSIC 4
|
||||||
|
#define KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS 12
|
||||||
|
#define KVM_DEV_RISCV_AIA_IMSIC_ISEL_MASK \
|
||||||
|
((1U << KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS) - 1)
|
||||||
|
#define KVM_DEV_RISCV_AIA_IMSIC_MKATTR(__vcpu, __isel) \
|
||||||
|
(((__vcpu) << KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS) | \
|
||||||
|
((__isel) & KVM_DEV_RISCV_AIA_IMSIC_ISEL_MASK))
|
||||||
|
#define KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(__attr) \
|
||||||
|
((__attr) & KVM_DEV_RISCV_AIA_IMSIC_ISEL_MASK)
|
||||||
|
#define KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(__attr) \
|
||||||
|
((__attr) >> KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS)
|
||||||
|
|
||||||
/* One single KVM irqchip, ie. the AIA */
|
/* One single KVM irqchip, ie. the AIA */
|
||||||
#define KVM_NR_IRQCHIPS 1
|
#define KVM_NR_IRQCHIPS 1
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ static int aia_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
|
|||||||
u32 nr;
|
u32 nr;
|
||||||
u64 addr;
|
u64 addr;
|
||||||
int nr_vcpus, r = -ENXIO;
|
int nr_vcpus, r = -ENXIO;
|
||||||
unsigned long type = (unsigned long)attr->attr;
|
unsigned long v, type = (unsigned long)attr->attr;
|
||||||
void __user *uaddr = (void __user *)(long)attr->addr;
|
void __user *uaddr = (void __user *)(long)attr->addr;
|
||||||
|
|
||||||
switch (attr->group) {
|
switch (attr->group) {
|
||||||
@ -374,6 +374,15 @@ static int aia_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
|
|||||||
r = kvm_riscv_aia_aplic_set_attr(dev->kvm, type, nr);
|
r = kvm_riscv_aia_aplic_set_attr(dev->kvm, type, nr);
|
||||||
mutex_unlock(&dev->kvm->lock);
|
mutex_unlock(&dev->kvm->lock);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case KVM_DEV_RISCV_AIA_GRP_IMSIC:
|
||||||
|
if (copy_from_user(&v, uaddr, sizeof(v)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
mutex_lock(&dev->kvm->lock);
|
||||||
|
r = kvm_riscv_aia_imsic_rw_attr(dev->kvm, type, true, &v);
|
||||||
|
mutex_unlock(&dev->kvm->lock);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,7 +395,7 @@ static int aia_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
|
|||||||
u64 addr;
|
u64 addr;
|
||||||
int nr_vcpus, r = -ENXIO;
|
int nr_vcpus, r = -ENXIO;
|
||||||
void __user *uaddr = (void __user *)(long)attr->addr;
|
void __user *uaddr = (void __user *)(long)attr->addr;
|
||||||
unsigned long type = (unsigned long)attr->attr;
|
unsigned long v, type = (unsigned long)attr->attr;
|
||||||
|
|
||||||
switch (attr->group) {
|
switch (attr->group) {
|
||||||
case KVM_DEV_RISCV_AIA_GRP_CONFIG:
|
case KVM_DEV_RISCV_AIA_GRP_CONFIG:
|
||||||
@ -435,6 +444,20 @@ static int aia_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
|
|||||||
if (copy_to_user(uaddr, &nr, sizeof(nr)))
|
if (copy_to_user(uaddr, &nr, sizeof(nr)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case KVM_DEV_RISCV_AIA_GRP_IMSIC:
|
||||||
|
if (copy_from_user(&v, uaddr, sizeof(v)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
mutex_lock(&dev->kvm->lock);
|
||||||
|
r = kvm_riscv_aia_imsic_rw_attr(dev->kvm, type, false, &v);
|
||||||
|
mutex_unlock(&dev->kvm->lock);
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (copy_to_user(uaddr, &v, sizeof(v)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,6 +496,8 @@ static int aia_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
|
|||||||
break;
|
break;
|
||||||
case KVM_DEV_RISCV_AIA_GRP_APLIC:
|
case KVM_DEV_RISCV_AIA_GRP_APLIC:
|
||||||
return kvm_riscv_aia_aplic_has_attr(dev->kvm, attr->attr);
|
return kvm_riscv_aia_aplic_has_attr(dev->kvm, attr->attr);
|
||||||
|
case KVM_DEV_RISCV_AIA_GRP_IMSIC:
|
||||||
|
return kvm_riscv_aia_imsic_has_attr(dev->kvm, attr->attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
@ -278,6 +278,33 @@ static u32 imsic_mrif_topei(struct imsic_mrif *mrif, u32 nr_eix, u32 nr_msis)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int imsic_mrif_isel_check(u32 nr_eix, unsigned long isel)
|
||||||
|
{
|
||||||
|
u32 num = 0;
|
||||||
|
|
||||||
|
switch (isel) {
|
||||||
|
case IMSIC_EIDELIVERY:
|
||||||
|
case IMSIC_EITHRESHOLD:
|
||||||
|
break;
|
||||||
|
case IMSIC_EIP0 ... IMSIC_EIP63:
|
||||||
|
num = isel - IMSIC_EIP0;
|
||||||
|
break;
|
||||||
|
case IMSIC_EIE0 ... IMSIC_EIE63:
|
||||||
|
num = isel - IMSIC_EIE0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -ENOENT;
|
||||||
|
};
|
||||||
|
#ifndef CONFIG_32BIT
|
||||||
|
if (num & 0x1)
|
||||||
|
return -EINVAL;
|
||||||
|
#endif
|
||||||
|
if ((num / 2) >= nr_eix)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int imsic_mrif_rmw(struct imsic_mrif *mrif, u32 nr_eix,
|
static int imsic_mrif_rmw(struct imsic_mrif *mrif, u32 nr_eix,
|
||||||
unsigned long isel, unsigned long *val,
|
unsigned long isel, unsigned long *val,
|
||||||
unsigned long new_val, unsigned long wr_mask)
|
unsigned long new_val, unsigned long wr_mask)
|
||||||
@ -408,6 +435,86 @@ static void imsic_vsfile_read(int vsfile_hgei, int vsfile_cpu, u32 nr_eix,
|
|||||||
imsic_vsfile_local_read, &idata, 1);
|
imsic_vsfile_local_read, &idata, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct imsic_vsfile_rw_data {
|
||||||
|
int hgei;
|
||||||
|
int isel;
|
||||||
|
bool write;
|
||||||
|
unsigned long val;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void imsic_vsfile_local_rw(void *data)
|
||||||
|
{
|
||||||
|
struct imsic_vsfile_rw_data *idata = data;
|
||||||
|
unsigned long new_hstatus, old_hstatus, old_vsiselect;
|
||||||
|
|
||||||
|
old_vsiselect = csr_read(CSR_VSISELECT);
|
||||||
|
old_hstatus = csr_read(CSR_HSTATUS);
|
||||||
|
new_hstatus = old_hstatus & ~HSTATUS_VGEIN;
|
||||||
|
new_hstatus |= ((unsigned long)idata->hgei) << HSTATUS_VGEIN_SHIFT;
|
||||||
|
csr_write(CSR_HSTATUS, new_hstatus);
|
||||||
|
|
||||||
|
switch (idata->isel) {
|
||||||
|
case IMSIC_EIDELIVERY:
|
||||||
|
if (idata->write)
|
||||||
|
imsic_vs_csr_write(IMSIC_EIDELIVERY, idata->val);
|
||||||
|
else
|
||||||
|
idata->val = imsic_vs_csr_read(IMSIC_EIDELIVERY);
|
||||||
|
break;
|
||||||
|
case IMSIC_EITHRESHOLD:
|
||||||
|
if (idata->write)
|
||||||
|
imsic_vs_csr_write(IMSIC_EITHRESHOLD, idata->val);
|
||||||
|
else
|
||||||
|
idata->val = imsic_vs_csr_read(IMSIC_EITHRESHOLD);
|
||||||
|
break;
|
||||||
|
case IMSIC_EIP0 ... IMSIC_EIP63:
|
||||||
|
case IMSIC_EIE0 ... IMSIC_EIE63:
|
||||||
|
#ifndef CONFIG_32BIT
|
||||||
|
if (idata->isel & 0x1)
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
if (idata->write)
|
||||||
|
imsic_eix_write(idata->isel, idata->val);
|
||||||
|
else
|
||||||
|
idata->val = imsic_eix_read(idata->isel);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
csr_write(CSR_HSTATUS, old_hstatus);
|
||||||
|
csr_write(CSR_VSISELECT, old_vsiselect);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int imsic_vsfile_rw(int vsfile_hgei, int vsfile_cpu, u32 nr_eix,
|
||||||
|
unsigned long isel, bool write,
|
||||||
|
unsigned long *val)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct imsic_vsfile_rw_data rdata;
|
||||||
|
|
||||||
|
/* We can only access register if we have a IMSIC VS-file */
|
||||||
|
if (vsfile_cpu < 0 || vsfile_hgei <= 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Check IMSIC register iselect */
|
||||||
|
rc = imsic_mrif_isel_check(nr_eix, isel);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* We can only access register on local CPU */
|
||||||
|
rdata.hgei = vsfile_hgei;
|
||||||
|
rdata.isel = isel;
|
||||||
|
rdata.write = write;
|
||||||
|
rdata.val = (write) ? *val : 0;
|
||||||
|
on_each_cpu_mask(cpumask_of(vsfile_cpu),
|
||||||
|
imsic_vsfile_local_rw, &rdata, 1);
|
||||||
|
|
||||||
|
if (!write)
|
||||||
|
*val = rdata.val;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void imsic_vsfile_local_clear(int vsfile_hgei, u32 nr_eix)
|
static void imsic_vsfile_local_clear(int vsfile_hgei, u32 nr_eix)
|
||||||
{
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
@ -759,6 +866,69 @@ int kvm_riscv_vcpu_aia_imsic_rmw(struct kvm_vcpu *vcpu, unsigned long isel,
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_riscv_aia_imsic_rw_attr(struct kvm *kvm, unsigned long type,
|
||||||
|
bool write, unsigned long *val)
|
||||||
|
{
|
||||||
|
u32 isel, vcpu_id;
|
||||||
|
unsigned long flags;
|
||||||
|
struct imsic *imsic;
|
||||||
|
struct kvm_vcpu *vcpu;
|
||||||
|
int rc, vsfile_hgei, vsfile_cpu;
|
||||||
|
|
||||||
|
if (!kvm_riscv_aia_initialized(kvm))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
vcpu_id = KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(type);
|
||||||
|
vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id);
|
||||||
|
if (!vcpu)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
|
||||||
|
imsic = vcpu->arch.aia_context.imsic_state;
|
||||||
|
|
||||||
|
read_lock_irqsave(&imsic->vsfile_lock, flags);
|
||||||
|
|
||||||
|
rc = 0;
|
||||||
|
vsfile_hgei = imsic->vsfile_hgei;
|
||||||
|
vsfile_cpu = imsic->vsfile_cpu;
|
||||||
|
if (vsfile_cpu < 0) {
|
||||||
|
if (write) {
|
||||||
|
rc = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix,
|
||||||
|
isel, NULL, *val, -1UL);
|
||||||
|
imsic_swfile_extirq_update(vcpu);
|
||||||
|
} else
|
||||||
|
rc = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix,
|
||||||
|
isel, val, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
read_unlock_irqrestore(&imsic->vsfile_lock, flags);
|
||||||
|
|
||||||
|
if (!rc && vsfile_cpu >= 0)
|
||||||
|
rc = imsic_vsfile_rw(vsfile_hgei, vsfile_cpu, imsic->nr_eix,
|
||||||
|
isel, write, val);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int kvm_riscv_aia_imsic_has_attr(struct kvm *kvm, unsigned long type)
|
||||||
|
{
|
||||||
|
u32 isel, vcpu_id;
|
||||||
|
struct imsic *imsic;
|
||||||
|
struct kvm_vcpu *vcpu;
|
||||||
|
|
||||||
|
if (!kvm_riscv_aia_initialized(kvm))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
vcpu_id = KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(type);
|
||||||
|
vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id);
|
||||||
|
if (!vcpu)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
|
||||||
|
imsic = vcpu->arch.aia_context.imsic_state;
|
||||||
|
return imsic_mrif_isel_check(imsic->nr_eix, isel);
|
||||||
|
}
|
||||||
|
|
||||||
void kvm_riscv_vcpu_aia_imsic_reset(struct kvm_vcpu *vcpu)
|
void kvm_riscv_vcpu_aia_imsic_reset(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
|
struct imsic *imsic = vcpu->arch.aia_context.imsic_state;
|
||||||
|
Loading…
Reference in New Issue
Block a user