Skip to content

Commit 5463091

Browse files
committed
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>
1 parent db8b7e9 commit 5463091

4 files changed

Lines changed: 217 additions & 2 deletions

File tree

arch/riscv/include/asm/kvm_aia.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ int kvm_riscv_vcpu_aia_imsic_update(struct kvm_vcpu *vcpu);
9797
int kvm_riscv_vcpu_aia_imsic_rmw(struct kvm_vcpu *vcpu, unsigned long isel,
9898
unsigned long *val, unsigned long new_val,
9999
unsigned long wr_mask);
100+
int kvm_riscv_aia_imsic_rw_attr(struct kvm *kvm, unsigned long type,
101+
bool write, unsigned long *val);
102+
int kvm_riscv_aia_imsic_has_attr(struct kvm *kvm, unsigned long type);
100103
void kvm_riscv_vcpu_aia_imsic_reset(struct kvm_vcpu *vcpu);
101104
int kvm_riscv_vcpu_aia_imsic_inject(struct kvm_vcpu *vcpu,
102105
u32 guest_index, u32 offset, u32 iid);

arch/riscv/include/uapi/asm/kvm.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,23 @@ enum KVM_RISCV_SBI_EXT_ID {
255255
*/
256256
#define KVM_DEV_RISCV_AIA_GRP_APLIC 3
257257

258+
/*
259+
* The lower 12-bits of the device attribute type contains the iselect
260+
* value of the IMSIC register (range 0x70-0xFF) whereas the higher order
261+
* bits contains the VCPU id.
262+
*/
263+
#define KVM_DEV_RISCV_AIA_GRP_IMSIC 4
264+
#define KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS 12
265+
#define KVM_DEV_RISCV_AIA_IMSIC_ISEL_MASK \
266+
((1U << KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS) - 1)
267+
#define KVM_DEV_RISCV_AIA_IMSIC_MKATTR(__vcpu, __isel) \
268+
(((__vcpu) << KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS) | \
269+
((__isel) & KVM_DEV_RISCV_AIA_IMSIC_ISEL_MASK))
270+
#define KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(__attr) \
271+
((__attr) & KVM_DEV_RISCV_AIA_IMSIC_ISEL_MASK)
272+
#define KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(__attr) \
273+
((__attr) >> KVM_DEV_RISCV_AIA_IMSIC_ISEL_BITS)
274+
258275
/* One single KVM irqchip, ie. the AIA */
259276
#define KVM_NR_IRQCHIPS 1
260277

arch/riscv/kvm/aia_device.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ static int aia_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
327327
u32 nr;
328328
u64 addr;
329329
int nr_vcpus, r = -ENXIO;
330-
unsigned long type = (unsigned long)attr->attr;
330+
unsigned long v, type = (unsigned long)attr->attr;
331331
void __user *uaddr = (void __user *)(long)attr->addr;
332332

333333
switch (attr->group) {
@@ -374,6 +374,15 @@ static int aia_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
374374
r = kvm_riscv_aia_aplic_set_attr(dev->kvm, type, nr);
375375
mutex_unlock(&dev->kvm->lock);
376376

377+
break;
378+
case KVM_DEV_RISCV_AIA_GRP_IMSIC:
379+
if (copy_from_user(&v, uaddr, sizeof(v)))
380+
return -EFAULT;
381+
382+
mutex_lock(&dev->kvm->lock);
383+
r = kvm_riscv_aia_imsic_rw_attr(dev->kvm, type, true, &v);
384+
mutex_unlock(&dev->kvm->lock);
385+
377386
break;
378387
}
379388

@@ -386,7 +395,7 @@ static int aia_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
386395
u64 addr;
387396
int nr_vcpus, r = -ENXIO;
388397
void __user *uaddr = (void __user *)(long)attr->addr;
389-
unsigned long type = (unsigned long)attr->attr;
398+
unsigned long v, type = (unsigned long)attr->attr;
390399

391400
switch (attr->group) {
392401
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)
435444
if (copy_to_user(uaddr, &nr, sizeof(nr)))
436445
return -EFAULT;
437446

447+
break;
448+
case KVM_DEV_RISCV_AIA_GRP_IMSIC:
449+
if (copy_from_user(&v, uaddr, sizeof(v)))
450+
return -EFAULT;
451+
452+
mutex_lock(&dev->kvm->lock);
453+
r = kvm_riscv_aia_imsic_rw_attr(dev->kvm, type, false, &v);
454+
mutex_unlock(&dev->kvm->lock);
455+
if (r)
456+
return r;
457+
458+
if (copy_to_user(uaddr, &v, sizeof(v)))
459+
return -EFAULT;
460+
438461
break;
439462
}
440463

@@ -473,6 +496,8 @@ static int aia_has_attr(struct kvm_device *dev, struct kvm_device_attr *attr)
473496
break;
474497
case KVM_DEV_RISCV_AIA_GRP_APLIC:
475498
return kvm_riscv_aia_aplic_has_attr(dev->kvm, attr->attr);
499+
case KVM_DEV_RISCV_AIA_GRP_IMSIC:
500+
return kvm_riscv_aia_imsic_has_attr(dev->kvm, attr->attr);
476501
}
477502

478503
return -ENXIO;

arch/riscv/kvm/aia_imsic.c

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,33 @@ static u32 imsic_mrif_topei(struct imsic_mrif *mrif, u32 nr_eix, u32 nr_msis)
278278
return 0;
279279
}
280280

281+
static int imsic_mrif_isel_check(u32 nr_eix, unsigned long isel)
282+
{
283+
u32 num = 0;
284+
285+
switch (isel) {
286+
case IMSIC_EIDELIVERY:
287+
case IMSIC_EITHRESHOLD:
288+
break;
289+
case IMSIC_EIP0 ... IMSIC_EIP63:
290+
num = isel - IMSIC_EIP0;
291+
break;
292+
case IMSIC_EIE0 ... IMSIC_EIE63:
293+
num = isel - IMSIC_EIE0;
294+
break;
295+
default:
296+
return -ENOENT;
297+
};
298+
#ifndef CONFIG_32BIT
299+
if (num & 0x1)
300+
return -EINVAL;
301+
#endif
302+
if ((num / 2) >= nr_eix)
303+
return -EINVAL;
304+
305+
return 0;
306+
}
307+
281308
static int imsic_mrif_rmw(struct imsic_mrif *mrif, u32 nr_eix,
282309
unsigned long isel, unsigned long *val,
283310
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,
408435
imsic_vsfile_local_read, &idata, 1);
409436
}
410437

438+
struct imsic_vsfile_rw_data {
439+
int hgei;
440+
int isel;
441+
bool write;
442+
unsigned long val;
443+
};
444+
445+
static void imsic_vsfile_local_rw(void *data)
446+
{
447+
struct imsic_vsfile_rw_data *idata = data;
448+
unsigned long new_hstatus, old_hstatus, old_vsiselect;
449+
450+
old_vsiselect = csr_read(CSR_VSISELECT);
451+
old_hstatus = csr_read(CSR_HSTATUS);
452+
new_hstatus = old_hstatus & ~HSTATUS_VGEIN;
453+
new_hstatus |= ((unsigned long)idata->hgei) << HSTATUS_VGEIN_SHIFT;
454+
csr_write(CSR_HSTATUS, new_hstatus);
455+
456+
switch (idata->isel) {
457+
case IMSIC_EIDELIVERY:
458+
if (idata->write)
459+
imsic_vs_csr_write(IMSIC_EIDELIVERY, idata->val);
460+
else
461+
idata->val = imsic_vs_csr_read(IMSIC_EIDELIVERY);
462+
break;
463+
case IMSIC_EITHRESHOLD:
464+
if (idata->write)
465+
imsic_vs_csr_write(IMSIC_EITHRESHOLD, idata->val);
466+
else
467+
idata->val = imsic_vs_csr_read(IMSIC_EITHRESHOLD);
468+
break;
469+
case IMSIC_EIP0 ... IMSIC_EIP63:
470+
case IMSIC_EIE0 ... IMSIC_EIE63:
471+
#ifndef CONFIG_32BIT
472+
if (idata->isel & 0x1)
473+
break;
474+
#endif
475+
if (idata->write)
476+
imsic_eix_write(idata->isel, idata->val);
477+
else
478+
idata->val = imsic_eix_read(idata->isel);
479+
break;
480+
default:
481+
break;
482+
}
483+
484+
csr_write(CSR_HSTATUS, old_hstatus);
485+
csr_write(CSR_VSISELECT, old_vsiselect);
486+
}
487+
488+
static int imsic_vsfile_rw(int vsfile_hgei, int vsfile_cpu, u32 nr_eix,
489+
unsigned long isel, bool write,
490+
unsigned long *val)
491+
{
492+
int rc;
493+
struct imsic_vsfile_rw_data rdata;
494+
495+
/* We can only access register if we have a IMSIC VS-file */
496+
if (vsfile_cpu < 0 || vsfile_hgei <= 0)
497+
return -EINVAL;
498+
499+
/* Check IMSIC register iselect */
500+
rc = imsic_mrif_isel_check(nr_eix, isel);
501+
if (rc)
502+
return rc;
503+
504+
/* We can only access register on local CPU */
505+
rdata.hgei = vsfile_hgei;
506+
rdata.isel = isel;
507+
rdata.write = write;
508+
rdata.val = (write) ? *val : 0;
509+
on_each_cpu_mask(cpumask_of(vsfile_cpu),
510+
imsic_vsfile_local_rw, &rdata, 1);
511+
512+
if (!write)
513+
*val = rdata.val;
514+
515+
return 0;
516+
}
517+
411518
static void imsic_vsfile_local_clear(int vsfile_hgei, u32 nr_eix)
412519
{
413520
u32 i;
@@ -759,6 +866,69 @@ int kvm_riscv_vcpu_aia_imsic_rmw(struct kvm_vcpu *vcpu, unsigned long isel,
759866
return rc;
760867
}
761868

869+
int kvm_riscv_aia_imsic_rw_attr(struct kvm *kvm, unsigned long type,
870+
bool write, unsigned long *val)
871+
{
872+
u32 isel, vcpu_id;
873+
unsigned long flags;
874+
struct imsic *imsic;
875+
struct kvm_vcpu *vcpu;
876+
int rc, vsfile_hgei, vsfile_cpu;
877+
878+
if (!kvm_riscv_aia_initialized(kvm))
879+
return -ENODEV;
880+
881+
vcpu_id = KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(type);
882+
vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id);
883+
if (!vcpu)
884+
return -ENODEV;
885+
886+
isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
887+
imsic = vcpu->arch.aia_context.imsic_state;
888+
889+
read_lock_irqsave(&imsic->vsfile_lock, flags);
890+
891+
rc = 0;
892+
vsfile_hgei = imsic->vsfile_hgei;
893+
vsfile_cpu = imsic->vsfile_cpu;
894+
if (vsfile_cpu < 0) {
895+
if (write) {
896+
rc = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix,
897+
isel, NULL, *val, -1UL);
898+
imsic_swfile_extirq_update(vcpu);
899+
} else
900+
rc = imsic_mrif_rmw(imsic->swfile, imsic->nr_eix,
901+
isel, val, 0, 0);
902+
}
903+
904+
read_unlock_irqrestore(&imsic->vsfile_lock, flags);
905+
906+
if (!rc && vsfile_cpu >= 0)
907+
rc = imsic_vsfile_rw(vsfile_hgei, vsfile_cpu, imsic->nr_eix,
908+
isel, write, val);
909+
910+
return rc;
911+
}
912+
913+
int kvm_riscv_aia_imsic_has_attr(struct kvm *kvm, unsigned long type)
914+
{
915+
u32 isel, vcpu_id;
916+
struct imsic *imsic;
917+
struct kvm_vcpu *vcpu;
918+
919+
if (!kvm_riscv_aia_initialized(kvm))
920+
return -ENODEV;
921+
922+
vcpu_id = KVM_DEV_RISCV_AIA_IMSIC_GET_VCPU(type);
923+
vcpu = kvm_get_vcpu_by_id(kvm, vcpu_id);
924+
if (!vcpu)
925+
return -ENODEV;
926+
927+
isel = KVM_DEV_RISCV_AIA_IMSIC_GET_ISEL(type);
928+
imsic = vcpu->arch.aia_context.imsic_state;
929+
return imsic_mrif_isel_check(imsic->nr_eix, isel);
930+
}
931+
762932
void kvm_riscv_vcpu_aia_imsic_reset(struct kvm_vcpu *vcpu)
763933
{
764934
struct imsic *imsic = vcpu->arch.aia_context.imsic_state;

0 commit comments

Comments
 (0)