1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2020-2023 Loongson Technology Corporation Limited 4 */ 5 6 #include <linux/err.h> 7 #include <linux/errno.h> 8 #include <asm/kvm_csr.h> 9 #include <asm/kvm_vcpu.h> 10 #include <asm/kvm_dmsintc.h> 11 12 static void kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned long mask) 13 { 14 unsigned long irq; 15 unsigned long old, new; 16 17 if (mask & CPU_AVEC) 18 dmsintc_inject_irq(vcpu); 19 20 irq = mask & KVM_ESTAT_INTI_MASK; 21 if (irq) { 22 old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); 23 set_gcsr_estat(irq); 24 new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); 25 26 /* Inject TI if TVAL inverted */ 27 if (new > old) 28 set_gcsr_estat(CPU_TIMER); 29 } 30 31 irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK; 32 if (irq) 33 set_csr_gintc(irq); 34 } 35 36 static void kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned long mask) 37 { 38 unsigned long irq; 39 unsigned long old, new; 40 41 irq = mask & KVM_ESTAT_INTI_MASK; 42 if (irq) { 43 old = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); 44 clear_gcsr_estat(irq); 45 new = kvm_read_hw_gcsr(LOONGARCH_CSR_TVAL); 46 47 /* Inject TI if TVAL inverted */ 48 if (new > old) 49 set_gcsr_estat(CPU_TIMER); 50 } 51 52 irq = (mask >> VIP_DELTA) & KVM_GINTC_IRQ_MASK; 53 if (irq) 54 clear_csr_gintc(irq); 55 } 56 57 void kvm_deliver_intr(struct kvm_vcpu *vcpu) 58 { 59 unsigned long mask; 60 61 mask = READ_ONCE(vcpu->arch.irq_clear); 62 if (mask) { 63 mask = xchg_relaxed(&vcpu->arch.irq_clear, 0); 64 kvm_irq_clear(vcpu, mask); 65 } 66 67 mask = READ_ONCE(vcpu->arch.irq_pending); 68 if (mask) { 69 mask = xchg_relaxed(&vcpu->arch.irq_pending, 0); 70 kvm_irq_deliver(vcpu, mask); 71 } 72 } 73 74 int kvm_pending_timer(struct kvm_vcpu *vcpu) 75 { 76 return test_bit(INT_TI, &vcpu->arch.irq_pending); 77 } 78 79 /* 80 * Only support illegal instruction or illegal Address Error exception, 81 * Other exceptions are injected by hardware in kvm mode 82 */ 83 static void _kvm_deliver_exception(struct kvm_vcpu *vcpu, 84 unsigned int code, unsigned int subcode) 85 { 86 unsigned long val, vec_size; 87 88 /* 89 * BADV is added for EXCCODE_ADE exception 90 * Use PC register (GVA address) if it is instruction exeception 91 * Else use BADV from host side (GPA address) for data exeception 92 */ 93 if (code == EXCCODE_ADE) { 94 if (subcode == EXSUBCODE_ADEF) 95 val = vcpu->arch.pc; 96 else 97 val = vcpu->arch.badv; 98 kvm_write_hw_gcsr(LOONGARCH_CSR_BADV, val); 99 } 100 101 /* Set exception instruction */ 102 kvm_write_hw_gcsr(LOONGARCH_CSR_BADI, vcpu->arch.badi); 103 104 /* 105 * Save CRMD in PRMD 106 * Set IRQ disabled and PLV0 with CRMD 107 */ 108 val = kvm_read_hw_gcsr(LOONGARCH_CSR_CRMD); 109 kvm_write_hw_gcsr(LOONGARCH_CSR_PRMD, val); 110 val = val & ~(CSR_CRMD_PLV | CSR_CRMD_IE); 111 kvm_write_hw_gcsr(LOONGARCH_CSR_CRMD, val); 112 113 /* Set exception PC address */ 114 kvm_write_hw_gcsr(LOONGARCH_CSR_ERA, vcpu->arch.pc); 115 116 /* 117 * Set exception code 118 * Exception and interrupt can be inject at the same time 119 * Hardware will handle exception first and then extern interrupt 120 * Exception code is Ecode in ESTAT[16:21] 121 * Interrupt code in ESTAT[0:12] 122 */ 123 val = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT); 124 val = (val & ~CSR_ESTAT_EXC) | code; 125 kvm_write_hw_gcsr(LOONGARCH_CSR_ESTAT, val); 126 127 /* Calculate expcetion entry address */ 128 val = kvm_read_hw_gcsr(LOONGARCH_CSR_ECFG); 129 vec_size = (val & CSR_ECFG_VS) >> CSR_ECFG_VS_SHIFT; 130 if (vec_size) 131 vec_size = (1 << vec_size) * 4; 132 val = kvm_read_hw_gcsr(LOONGARCH_CSR_EENTRY); 133 vcpu->arch.pc = val + code * vec_size; 134 } 135 136 void kvm_deliver_exception(struct kvm_vcpu *vcpu) 137 { 138 unsigned int code; 139 unsigned long *pending = &vcpu->arch.exception_pending; 140 141 if (*pending) { 142 code = __ffs(*pending); 143 _kvm_deliver_exception(vcpu, code, vcpu->arch.esubcode); 144 *pending = 0; 145 vcpu->arch.esubcode = 0; 146 } 147 } 148