| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2018 - os kernal |
| * Author: fire3 <fire3@example.com> yangzh <yangzh@gmail.com> |
| * linhn <linhn@example.com> |
| */ |
| #include <asm/kvm_emulate.h> |
| #include <linux/errno.h> |
| #include <linux/err.h> |
| |
| void sw64_decode(struct kvm_vcpu *vcpu, unsigned int insn, struct kvm_run *run) |
| { |
| int opc, ra; |
| |
| opc = (insn >> 26) & 0x3f; |
| ra = (insn >> 21) & 0x1f; |
| |
| switch (opc) { |
| case 0x20: /* LDBU */ |
| run->mmio.is_write = 0; |
| run->mmio.len = 1; |
| vcpu->arch.mmio_decode.rt = ra; |
| break; |
| case 0x21: /* LDHU */ |
| run->mmio.is_write = 0; |
| run->mmio.len = 2; |
| vcpu->arch.mmio_decode.rt = ra; |
| break; |
| case 0x22: /* LDW */ |
| run->mmio.is_write = 0; |
| run->mmio.len = 4; |
| vcpu->arch.mmio_decode.rt = ra; |
| break; |
| case 0x23: /* LDL */ |
| case 0x24: /* LDL_U */ |
| run->mmio.is_write = 0; |
| run->mmio.len = 8; |
| vcpu->arch.mmio_decode.rt = ra; |
| break; |
| case 0x28: /* STB */ |
| run->mmio.is_write = 1; |
| *(unsigned long *)run->mmio.data = vcpu_get_reg(vcpu, ra) & 0xffUL; |
| run->mmio.len = 1; |
| break; |
| case 0x29: /* STH */ |
| run->mmio.is_write = 1; |
| *(unsigned long *)run->mmio.data = vcpu_get_reg(vcpu, ra) & 0xffffUL; |
| run->mmio.len = 2; |
| break; |
| case 0x2a: /* STW */ |
| run->mmio.is_write = 1; |
| *(unsigned long *)run->mmio.data = vcpu_get_reg(vcpu, ra) & 0xffffffffUL; |
| run->mmio.len = 4; |
| break; |
| case 0x2b: /* STL */ |
| case 0x2c: /* STL_U */ |
| run->mmio.is_write = 1; |
| *(unsigned long *)run->mmio.data = vcpu_get_reg(vcpu, ra); |
| run->mmio.len = 8; |
| break; |
| default: |
| printk("Miss done opc %d\n", opc); |
| break; |
| } |
| } |
| |
| /* |
| * Virtual Interrupts. |
| */ |
| unsigned int interrupt_pending(struct kvm_vcpu *vcpu, bool *more) |
| { |
| unsigned int irq; |
| DECLARE_BITMAP(blk, SWVM_IRQS); |
| |
| bitmap_copy(blk, vcpu->arch.irqs_pending, SWVM_IRQS); |
| |
| irq = find_last_bit(blk, SWVM_IRQS); |
| |
| return irq; |
| } |
| |
| void clear_vcpu_irq(struct kvm_vcpu *vcpu) |
| { |
| vcpu->arch.vcb.vcpu_irq = 0xffffffffffffffffUL; |
| } |
| |
| void inject_vcpu_irq(struct kvm_vcpu *vcpu, unsigned int irq) |
| { |
| vcpu->arch.vcb.vcpu_irq = irq; |
| } |
| |
| /* |
| * This actually diverts the Guest to running an interrupt handler, once an |
| * interrupt has been identified by interrupt_pending(). |
| */ |
| void try_deliver_interrupt(struct kvm_vcpu *vcpu, unsigned int irq, bool more) |
| { |
| BUG_ON(irq >= SWVM_IRQS); |
| |
| /* Otherwise we check if they have interrupts disabled. */ |
| if (vcpu->arch.vcb.vcpu_irq_disabled) { |
| clear_vcpu_irq(vcpu); |
| return; |
| } |
| |
| /* If they don't have a handler (yet?), we just ignore it */ |
| if (vcpu->arch.vcb.ent_int != 0) { |
| /* OK, mark it no longer pending and deliver it. */ |
| clear_bit(irq, (vcpu->arch.irqs_pending)); |
| /* |
| * set_guest_interrupt() takes the interrupt descriptor and a |
| * flag to say whether this interrupt pushes an error code onto |
| * the stack as well: virtual interrupts never do. |
| */ |
| inject_vcpu_irq(vcpu, irq); |
| } |
| } |