interrupt.c 4.1 KB

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