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