LoongArch: KVM: Implement handle iocsr exception
authorTianrui Zhao <zhaotianrui@loongson.cn>
Mon, 2 Oct 2023 02:01:28 +0000 (10:01 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Mon, 2 Oct 2023 02:01:28 +0000 (10:01 +0800)
Implement kvm handle vcpu iocsr exception, setting the iocsr info into
vcpu_run and return to user space to handle it.

Reviewed-by: Bibo Mao <maobibo@loongson.cn>
Tested-by: Huacai Chen <chenhuacai@loongson.cn>
Signed-off-by: Tianrui Zhao <zhaotianrui@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/include/asm/inst.h
arch/loongarch/kvm/exit.c

index 71e1ed4165c80d8a753309fcb381b394dfa85665..008a88ead60d9a55a4a8ffb0640b53bff5a127ba 100644 (file)
@@ -65,6 +65,14 @@ enum reg2_op {
        revbd_op        = 0x0f,
        revh2w_op       = 0x10,
        revhd_op        = 0x11,
+       iocsrrdb_op     = 0x19200,
+       iocsrrdh_op     = 0x19201,
+       iocsrrdw_op     = 0x19202,
+       iocsrrdd_op     = 0x19203,
+       iocsrwrb_op     = 0x19204,
+       iocsrwrh_op     = 0x19205,
+       iocsrwrw_op     = 0x19206,
+       iocsrwrd_op     = 0x19207,
 };
 
 enum reg2i5_op {
@@ -318,6 +326,13 @@ struct reg2bstrd_format {
        unsigned int opcode : 10;
 };
 
+struct reg2csr_format {
+       unsigned int rd : 5;
+       unsigned int rj : 5;
+       unsigned int csr : 14;
+       unsigned int opcode : 8;
+};
+
 struct reg3_format {
        unsigned int rd : 5;
        unsigned int rj : 5;
@@ -346,6 +361,7 @@ union loongarch_instruction {
        struct reg2i14_format   reg2i14_format;
        struct reg2i16_format   reg2i16_format;
        struct reg2bstrd_format reg2bstrd_format;
+       struct reg2csr_format   reg2csr_format;
        struct reg3_format      reg3_format;
        struct reg3sa2_format   reg3sa2_format;
 };
index 37bc8a4209bd397d966f9b38780c1a94cd59a36d..7e729dd9e91581de00169f7ae8e6d57514081f1e 100644 (file)
@@ -103,3 +103,94 @@ static int kvm_handle_csr(struct kvm_vcpu *vcpu, larch_inst inst)
 
        return EMULATE_DONE;
 }
+
+int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu)
+{
+       int ret;
+       unsigned long val;
+       u32 addr, rd, rj, opcode;
+
+       /*
+        * Each IOCSR with different opcode
+        */
+       rd = inst.reg2_format.rd;
+       rj = inst.reg2_format.rj;
+       opcode = inst.reg2_format.opcode;
+       addr = vcpu->arch.gprs[rj];
+       ret = EMULATE_DO_IOCSR;
+       run->iocsr_io.phys_addr = addr;
+       run->iocsr_io.is_write = 0;
+
+       /* LoongArch is Little endian */
+       switch (opcode) {
+       case iocsrrdb_op:
+               run->iocsr_io.len = 1;
+               break;
+       case iocsrrdh_op:
+               run->iocsr_io.len = 2;
+               break;
+       case iocsrrdw_op:
+               run->iocsr_io.len = 4;
+               break;
+       case iocsrrdd_op:
+               run->iocsr_io.len = 8;
+               break;
+       case iocsrwrb_op:
+               run->iocsr_io.len = 1;
+               run->iocsr_io.is_write = 1;
+               break;
+       case iocsrwrh_op:
+               run->iocsr_io.len = 2;
+               run->iocsr_io.is_write = 1;
+               break;
+       case iocsrwrw_op:
+               run->iocsr_io.len = 4;
+               run->iocsr_io.is_write = 1;
+               break;
+       case iocsrwrd_op:
+               run->iocsr_io.len = 8;
+               run->iocsr_io.is_write = 1;
+               break;
+       default:
+               ret = EMULATE_FAIL;
+               break;
+       }
+
+       if (ret == EMULATE_DO_IOCSR) {
+               if (run->iocsr_io.is_write) {
+                       val = vcpu->arch.gprs[rd];
+                       memcpy(run->iocsr_io.data, &val, run->iocsr_io.len);
+               }
+               vcpu->arch.io_gpr = rd;
+       }
+
+       return ret;
+}
+
+int kvm_complete_iocsr_read(struct kvm_vcpu *vcpu, struct kvm_run *run)
+{
+       enum emulation_result er = EMULATE_DONE;
+       unsigned long *gpr = &vcpu->arch.gprs[vcpu->arch.io_gpr];
+
+       switch (run->iocsr_io.len) {
+       case 1:
+               *gpr = *(s8 *)run->iocsr_io.data;
+               break;
+       case 2:
+               *gpr = *(s16 *)run->iocsr_io.data;
+               break;
+       case 4:
+               *gpr = *(s32 *)run->iocsr_io.data;
+               break;
+       case 8:
+               *gpr = *(s64 *)run->iocsr_io.data;
+               break;
+       default:
+               kvm_err("Bad IOCSR length: %d, addr is 0x%lx\n",
+                               run->iocsr_io.len, vcpu->arch.badv);
+               er = EMULATE_FAIL;
+               break;
+       }
+
+       return er;
+}