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 11 static unsigned int priority_to_irq[EXCCODE_INT_NUM] = { 12 [INT_TI] = CPU_TIMER, 13 [INT_IPI] = CPU_IPI, 14 [INT_SWI0] = CPU_SIP0, 15 [INT_SWI1] = CPU_SIP1, 16 [INT_HWI0] = CPU_IP0, 17 [INT_HWI1] = CPU_IP1, 18 [INT_HWI2] = CPU_IP2, 19 [INT_HWI3] = CPU_IP3, 20 [INT_HWI4] = CPU_IP4, 21 [INT_HWI5] = CPU_IP5, 22 [INT_HWI6] = CPU_IP6, 23 [INT_HWI7] = CPU_IP7, 24 [INT_AVEC] = CPU_AVEC, 25 }; 26 27 static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority) 28 { 29 unsigned int irq = 0; 30 31 clear_bit(priority, &vcpu->arch.irq_pending); 32 if (priority < EXCCODE_INT_NUM) 33 irq = priority_to_irq[priority]; 34 35 if (cpu_has_msgint && (priority == INT_AVEC)) { 36 set_gcsr_estat(irq); 37 return 1; 38 } 39 40 switch (priority) { 41 case INT_TI: 42 case INT_IPI: 43 case INT_SWI0: 44 case INT_SWI1: 45 set_gcsr_estat(irq); 46 break; 47 48 case INT_HWI0 ... INT_HWI7: 49 set_csr_gintc(irq); 50 break; 51 52 default: 53 break; 54 } 55 56 return 1; 57 } 58 59 static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority) 60 { 61 unsigned int irq = 0; 62 63 clear_bit(priority, &vcpu->arch.irq_clear); 64 if (priority < EXCCODE_INT_NUM) 65 irq = priority_to_irq[priority]; 66 67 if (cpu_has_msgint && (priority == INT_AVEC)) { 68 clear_gcsr_estat(irq); 69 return 1; 70 } 71 72 switch (priority) { 73 case INT_TI: 74 case INT_IPI: 75 case INT_SWI0: 76 case INT_SWI1: 77 clear_gcsr_estat(irq); 78 break; 79 80 case INT_HWI0 ... INT_HWI7: 81 clear_csr_gintc(irq); 82 break; 83 84 default: 85 break; 86 } 87 88 return 1; 89 } 90 91 void kvm_deliver_intr(struct kvm_vcpu *vcpu) 92 { 93 unsigned int priority; 94 unsigned long *pending = &vcpu->arch.irq_pending; 95 unsigned long *pending_clr = &vcpu->arch.irq_clear; 96 97 for_each_set_bit(priority, pending_clr, EXCCODE_INT_NUM) 98 kvm_irq_clear(vcpu, priority); 99 100 for_each_set_bit(priority, pending, EXCCODE_INT_NUM) 101 kvm_irq_deliver(vcpu, priority); 102 } 103 104 int kvm_pending_timer(struct kvm_vcpu *vcpu) 105 { 106 return test_bit(INT_TI, &vcpu->arch.irq_pending); 107 } 108 109 /* 110 * Only support illegal instruction or illegal Address Error exception, 111 * Other exceptions are injected by hardware in kvm mode 112 */ 113 static void _kvm_deliver_exception(struct kvm_vcpu *vcpu, 114 unsigned int code, unsigned int subcode) 115 { 116 unsigned long val, vec_size; 117 118 /* 119 * BADV is added for EXCCODE_ADE exception 120 * Use PC register (GVA address) if it is instruction exeception 121 * Else use BADV from host side (GPA address) for data exeception 122 */ 123 if (code == EXCCODE_ADE) { 124 if (subcode == EXSUBCODE_ADEF) 125 val = vcpu->arch.pc; 126 else 127 val = vcpu->arch.badv; 128 kvm_write_hw_gcsr(LOONGARCH_CSR_BADV, val); 129 } 130 131 /* Set exception instruction */ 132 kvm_write_hw_gcsr(LOONGARCH_CSR_BADI, vcpu->arch.badi); 133 134 /* 135 * Save CRMD in PRMD 136 * Set IRQ disabled and PLV0 with CRMD 137 */ 138 val = kvm_read_hw_gcsr(LOONGARCH_CSR_CRMD); 139 kvm_write_hw_gcsr(LOONGARCH_CSR_PRMD, val); 140 val = val & ~(CSR_CRMD_PLV | CSR_CRMD_IE); 141 kvm_write_hw_gcsr(LOONGARCH_CSR_CRMD, val); 142 143 /* Set exception PC address */ 144 kvm_write_hw_gcsr(LOONGARCH_CSR_ERA, vcpu->arch.pc); 145 146 /* 147 * Set exception code 148 * Exception and interrupt can be inject at the same time 149 * Hardware will handle exception first and then extern interrupt 150 * Exception code is Ecode in ESTAT[16:21] 151 * Interrupt code in ESTAT[0:12] 152 */ 153 val = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT); 154 val = (val & ~CSR_ESTAT_EXC) | code; 155 kvm_write_hw_gcsr(LOONGARCH_CSR_ESTAT, val); 156 157 /* Calculate expcetion entry address */ 158 val = kvm_read_hw_gcsr(LOONGARCH_CSR_ECFG); 159 vec_size = (val & CSR_ECFG_VS) >> CSR_ECFG_VS_SHIFT; 160 if (vec_size) 161 vec_size = (1 << vec_size) * 4; 162 val = kvm_read_hw_gcsr(LOONGARCH_CSR_EENTRY); 163 vcpu->arch.pc = val + code * vec_size; 164 } 165 166 void kvm_deliver_exception(struct kvm_vcpu *vcpu) 167 { 168 unsigned int code; 169 unsigned long *pending = &vcpu->arch.exception_pending; 170 171 if (*pending) { 172 code = __ffs(*pending); 173 _kvm_deliver_exception(vcpu, code, vcpu->arch.esubcode); 174 *pending = 0; 175 vcpu->arch.esubcode = 0; 176 } 177 } 178