| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
- */
- #include <linux/err.h>
- #include <linux/errno.h>
- #include <asm/kvm_csr.h>
- #include <asm/kvm_vcpu.h>
- static unsigned int priority_to_irq[EXCCODE_INT_NUM] = {
- [INT_TI] = CPU_TIMER,
- [INT_IPI] = CPU_IPI,
- [INT_SWI0] = CPU_SIP0,
- [INT_SWI1] = CPU_SIP1,
- [INT_HWI0] = CPU_IP0,
- [INT_HWI1] = CPU_IP1,
- [INT_HWI2] = CPU_IP2,
- [INT_HWI3] = CPU_IP3,
- [INT_HWI4] = CPU_IP4,
- [INT_HWI5] = CPU_IP5,
- [INT_HWI6] = CPU_IP6,
- [INT_HWI7] = CPU_IP7,
- };
- static int kvm_irq_deliver(struct kvm_vcpu *vcpu, unsigned int priority)
- {
- unsigned int irq = 0;
- clear_bit(priority, &vcpu->arch.irq_pending);
- if (priority < EXCCODE_INT_NUM)
- irq = priority_to_irq[priority];
- switch (priority) {
- case INT_TI:
- case INT_IPI:
- case INT_SWI0:
- case INT_SWI1:
- set_gcsr_estat(irq);
- break;
- case INT_HWI0 ... INT_HWI7:
- set_csr_gintc(irq);
- break;
- default:
- break;
- }
- return 1;
- }
- static int kvm_irq_clear(struct kvm_vcpu *vcpu, unsigned int priority)
- {
- unsigned int irq = 0;
- clear_bit(priority, &vcpu->arch.irq_clear);
- if (priority < EXCCODE_INT_NUM)
- irq = priority_to_irq[priority];
- switch (priority) {
- case INT_TI:
- case INT_IPI:
- case INT_SWI0:
- case INT_SWI1:
- clear_gcsr_estat(irq);
- break;
- case INT_HWI0 ... INT_HWI7:
- clear_csr_gintc(irq);
- break;
- default:
- break;
- }
- return 1;
- }
- void kvm_deliver_intr(struct kvm_vcpu *vcpu)
- {
- unsigned int priority;
- unsigned long *pending = &vcpu->arch.irq_pending;
- unsigned long *pending_clr = &vcpu->arch.irq_clear;
- if (!(*pending) && !(*pending_clr))
- return;
- if (*pending_clr) {
- priority = __ffs(*pending_clr);
- while (priority <= INT_IPI) {
- kvm_irq_clear(vcpu, priority);
- priority = find_next_bit(pending_clr,
- BITS_PER_BYTE * sizeof(*pending_clr),
- priority + 1);
- }
- }
- if (*pending) {
- priority = __ffs(*pending);
- while (priority <= INT_IPI) {
- kvm_irq_deliver(vcpu, priority);
- priority = find_next_bit(pending,
- BITS_PER_BYTE * sizeof(*pending),
- priority + 1);
- }
- }
- }
- int kvm_pending_timer(struct kvm_vcpu *vcpu)
- {
- return test_bit(INT_TI, &vcpu->arch.irq_pending);
- }
- /*
- * Only support illegal instruction or illegal Address Error exception,
- * Other exceptions are injected by hardware in kvm mode
- */
- static void _kvm_deliver_exception(struct kvm_vcpu *vcpu,
- unsigned int code, unsigned int subcode)
- {
- unsigned long val, vec_size;
- /*
- * BADV is added for EXCCODE_ADE exception
- * Use PC register (GVA address) if it is instruction exeception
- * Else use BADV from host side (GPA address) for data exeception
- */
- if (code == EXCCODE_ADE) {
- if (subcode == EXSUBCODE_ADEF)
- val = vcpu->arch.pc;
- else
- val = vcpu->arch.badv;
- kvm_write_hw_gcsr(LOONGARCH_CSR_BADV, val);
- }
- /* Set exception instruction */
- kvm_write_hw_gcsr(LOONGARCH_CSR_BADI, vcpu->arch.badi);
- /*
- * Save CRMD in PRMD
- * Set IRQ disabled and PLV0 with CRMD
- */
- val = kvm_read_hw_gcsr(LOONGARCH_CSR_CRMD);
- kvm_write_hw_gcsr(LOONGARCH_CSR_PRMD, val);
- val = val & ~(CSR_CRMD_PLV | CSR_CRMD_IE);
- kvm_write_hw_gcsr(LOONGARCH_CSR_CRMD, val);
- /* Set exception PC address */
- kvm_write_hw_gcsr(LOONGARCH_CSR_ERA, vcpu->arch.pc);
- /*
- * Set exception code
- * Exception and interrupt can be inject at the same time
- * Hardware will handle exception first and then extern interrupt
- * Exception code is Ecode in ESTAT[16:21]
- * Interrupt code in ESTAT[0:12]
- */
- val = kvm_read_hw_gcsr(LOONGARCH_CSR_ESTAT);
- val = (val & ~CSR_ESTAT_EXC) | code;
- kvm_write_hw_gcsr(LOONGARCH_CSR_ESTAT, val);
- /* Calculate expcetion entry address */
- val = kvm_read_hw_gcsr(LOONGARCH_CSR_ECFG);
- vec_size = (val & CSR_ECFG_VS) >> CSR_ECFG_VS_SHIFT;
- if (vec_size)
- vec_size = (1 << vec_size) * 4;
- val = kvm_read_hw_gcsr(LOONGARCH_CSR_EENTRY);
- vcpu->arch.pc = val + code * vec_size;
- }
- void kvm_deliver_exception(struct kvm_vcpu *vcpu)
- {
- unsigned int code;
- unsigned long *pending = &vcpu->arch.exception_pending;
- if (*pending) {
- code = __ffs(*pending);
- _kvm_deliver_exception(vcpu, code, vcpu->arch.esubcode);
- *pending = 0;
- vcpu->arch.esubcode = 0;
- }
- }
|