xen_apic.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /*
  2. * Copyright (c) 2014 Roger Pau Monné <roger.pau@citrix.com>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
  15. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  17. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  18. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  20. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  21. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  22. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  23. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  24. * SUCH DAMAGE.
  25. */
  26. #include <sys/param.h>
  27. #include <sys/bus.h>
  28. #include <sys/kernel.h>
  29. #include <sys/malloc.h>
  30. #include <sys/proc.h>
  31. #include <sys/smp.h>
  32. #include <sys/systm.h>
  33. #include <vm/vm.h>
  34. #include <vm/pmap.h>
  35. #include <machine/cpufunc.h>
  36. #include <machine/cpu.h>
  37. #include <machine/intr_machdep.h>
  38. #include <machine/md_var.h>
  39. #include <machine/smp.h>
  40. #include <x86/apicreg.h>
  41. #include <x86/apicvar.h>
  42. #include <xen/xen-os.h>
  43. #include <xen/features.h>
  44. #include <xen/gnttab.h>
  45. #include <xen/hypervisor.h>
  46. #include <xen/hvm.h>
  47. #include <xen/xen_intr.h>
  48. #include <contrib/xen/arch-x86/cpuid.h>
  49. #include <contrib/xen/vcpu.h>
  50. /*--------------------------- Forward Declarations ---------------------------*/
  51. static driver_filter_t xen_smp_rendezvous_action;
  52. #ifdef __amd64__
  53. static driver_filter_t xen_invlop;
  54. #else
  55. static driver_filter_t xen_invltlb;
  56. static driver_filter_t xen_invlpg;
  57. static driver_filter_t xen_invlrng;
  58. static driver_filter_t xen_invlcache;
  59. #endif
  60. static driver_filter_t xen_ipi_bitmap_handler;
  61. static driver_filter_t xen_cpustop_handler;
  62. static driver_filter_t xen_cpususpend_handler;
  63. static driver_filter_t xen_ipi_swi_handler;
  64. /*---------------------------------- Macros ----------------------------------*/
  65. #define IPI_TO_IDX(ipi) ((ipi) - APIC_IPI_INTS)
  66. /*--------------------------------- Xen IPIs ---------------------------------*/
  67. struct xen_ipi_handler
  68. {
  69. driver_filter_t *filter;
  70. const char *description;
  71. };
  72. static struct xen_ipi_handler xen_ipis[] =
  73. {
  74. [IPI_TO_IDX(IPI_RENDEZVOUS)] = { xen_smp_rendezvous_action, "r" },
  75. #ifdef __amd64__
  76. [IPI_TO_IDX(IPI_INVLOP)] = { xen_invlop, "itlb"},
  77. #else
  78. [IPI_TO_IDX(IPI_INVLTLB)] = { xen_invltlb, "itlb"},
  79. [IPI_TO_IDX(IPI_INVLPG)] = { xen_invlpg, "ipg" },
  80. [IPI_TO_IDX(IPI_INVLRNG)] = { xen_invlrng, "irg" },
  81. [IPI_TO_IDX(IPI_INVLCACHE)] = { xen_invlcache, "ic" },
  82. #endif
  83. [IPI_TO_IDX(IPI_BITMAP_VECTOR)] = { xen_ipi_bitmap_handler, "b" },
  84. [IPI_TO_IDX(IPI_STOP)] = { xen_cpustop_handler, "st" },
  85. [IPI_TO_IDX(IPI_SUSPEND)] = { xen_cpususpend_handler, "sp" },
  86. [IPI_TO_IDX(IPI_SWI)] = { xen_ipi_swi_handler, "sw" },
  87. };
  88. /*
  89. * Save previous (native) handler as a fallback. Xen < 4.7 doesn't support
  90. * VCPUOP_send_nmi for HVM guests, and thus we need a fallback in that case:
  91. *
  92. * https://lists.freebsd.org/archives/freebsd-xen/2022-January/000032.html
  93. */
  94. void (*native_ipi_vectored)(u_int, int);
  95. /*------------------------------- Per-CPU Data -------------------------------*/
  96. DPCPU_DEFINE(xen_intr_handle_t, ipi_handle[nitems(xen_ipis)]);
  97. /*------------------------------- Xen PV APIC --------------------------------*/
  98. #define PCPU_ID_GET(id, field) (pcpu_find(id)->pc_##field)
  99. static int
  100. send_nmi(int dest)
  101. {
  102. unsigned int cpu;
  103. int rc = 0;
  104. /*
  105. * NMIs are not routed over event channels, and instead delivered as on
  106. * native using the exception vector (#2). Triggering them can be done
  107. * using the local APIC, or an hypercall as a shortcut like it's done
  108. * below.
  109. */
  110. switch(dest) {
  111. case APIC_IPI_DEST_SELF:
  112. rc = HYPERVISOR_vcpu_op(VCPUOP_send_nmi, PCPU_GET(vcpu_id), NULL);
  113. break;
  114. case APIC_IPI_DEST_ALL:
  115. CPU_FOREACH(cpu) {
  116. rc = HYPERVISOR_vcpu_op(VCPUOP_send_nmi,
  117. PCPU_ID_GET(cpu, vcpu_id), NULL);
  118. if (rc != 0)
  119. break;
  120. }
  121. break;
  122. case APIC_IPI_DEST_OTHERS:
  123. CPU_FOREACH(cpu) {
  124. if (cpu != PCPU_GET(cpuid)) {
  125. rc = HYPERVISOR_vcpu_op(VCPUOP_send_nmi,
  126. PCPU_ID_GET(cpu, vcpu_id), NULL);
  127. if (rc != 0)
  128. break;
  129. }
  130. }
  131. break;
  132. default:
  133. rc = HYPERVISOR_vcpu_op(VCPUOP_send_nmi,
  134. PCPU_ID_GET(apic_cpuid(dest), vcpu_id), NULL);
  135. break;
  136. }
  137. return rc;
  138. }
  139. #undef PCPU_ID_GET
  140. static void
  141. xen_pv_lapic_ipi_vectored(u_int vector, int dest)
  142. {
  143. xen_intr_handle_t *ipi_handle;
  144. int ipi_idx, to_cpu, self;
  145. static bool pvnmi = true;
  146. if (vector >= IPI_NMI_FIRST) {
  147. if (pvnmi) {
  148. int rc = send_nmi(dest);
  149. if (rc != 0) {
  150. printf(
  151. "Sending NMI using hypercall failed (%d) switching to APIC\n", rc);
  152. pvnmi = false;
  153. native_ipi_vectored(vector, dest);
  154. }
  155. } else
  156. native_ipi_vectored(vector, dest);
  157. return;
  158. }
  159. ipi_idx = IPI_TO_IDX(vector);
  160. if (ipi_idx >= nitems(xen_ipis))
  161. panic("IPI out of range");
  162. switch(dest) {
  163. case APIC_IPI_DEST_SELF:
  164. ipi_handle = DPCPU_GET(ipi_handle);
  165. xen_intr_signal(ipi_handle[ipi_idx]);
  166. break;
  167. case APIC_IPI_DEST_ALL:
  168. CPU_FOREACH(to_cpu) {
  169. ipi_handle = DPCPU_ID_GET(to_cpu, ipi_handle);
  170. xen_intr_signal(ipi_handle[ipi_idx]);
  171. }
  172. break;
  173. case APIC_IPI_DEST_OTHERS:
  174. self = PCPU_GET(cpuid);
  175. CPU_FOREACH(to_cpu) {
  176. if (to_cpu != self) {
  177. ipi_handle = DPCPU_ID_GET(to_cpu, ipi_handle);
  178. xen_intr_signal(ipi_handle[ipi_idx]);
  179. }
  180. }
  181. break;
  182. default:
  183. to_cpu = apic_cpuid(dest);
  184. ipi_handle = DPCPU_ID_GET(to_cpu, ipi_handle);
  185. xen_intr_signal(ipi_handle[ipi_idx]);
  186. break;
  187. }
  188. }
  189. /*---------------------------- XEN PV IPI Handlers ---------------------------*/
  190. /*
  191. * These are C clones of the ASM functions found in apic_vector.
  192. */
  193. static int
  194. xen_ipi_bitmap_handler(void *arg)
  195. {
  196. ipi_bitmap_handler(*curthread->td_intr_frame);
  197. return (FILTER_HANDLED);
  198. }
  199. static int
  200. xen_smp_rendezvous_action(void *arg)
  201. {
  202. #ifdef COUNT_IPIS
  203. (*ipi_rendezvous_counts[PCPU_GET(cpuid)])++;
  204. #endif /* COUNT_IPIS */
  205. smp_rendezvous_action();
  206. return (FILTER_HANDLED);
  207. }
  208. #ifdef __amd64__
  209. static int
  210. xen_invlop(void *arg)
  211. {
  212. invlop_handler();
  213. return (FILTER_HANDLED);
  214. }
  215. #else /* __i386__ */
  216. static int
  217. xen_invltlb(void *arg)
  218. {
  219. invltlb_handler();
  220. return (FILTER_HANDLED);
  221. }
  222. static int
  223. xen_invlpg(void *arg)
  224. {
  225. invlpg_handler();
  226. return (FILTER_HANDLED);
  227. }
  228. static int
  229. xen_invlrng(void *arg)
  230. {
  231. invlrng_handler();
  232. return (FILTER_HANDLED);
  233. }
  234. static int
  235. xen_invlcache(void *arg)
  236. {
  237. invlcache_handler();
  238. return (FILTER_HANDLED);
  239. }
  240. #endif /* __amd64__ */
  241. static int
  242. xen_cpustop_handler(void *arg)
  243. {
  244. cpustop_handler();
  245. return (FILTER_HANDLED);
  246. }
  247. static int
  248. xen_cpususpend_handler(void *arg)
  249. {
  250. cpususpend_handler();
  251. return (FILTER_HANDLED);
  252. }
  253. static int
  254. xen_ipi_swi_handler(void *arg)
  255. {
  256. ipi_swi_handler(*curthread->td_intr_frame);
  257. return (FILTER_HANDLED);
  258. }
  259. /*----------------------------- XEN PV IPI setup -----------------------------*/
  260. /*
  261. * Those functions are provided outside of the Xen PV APIC implementation
  262. * so PVHVM guests can also use PV IPIs without having an actual Xen PV APIC,
  263. * because on PVHVM there's an emulated LAPIC provided by Xen.
  264. */
  265. static void
  266. xen_cpu_ipi_init(int cpu)
  267. {
  268. xen_intr_handle_t *ipi_handle;
  269. const struct xen_ipi_handler *ipi;
  270. int idx, rc;
  271. ipi_handle = DPCPU_ID_GET(cpu, ipi_handle);
  272. for (ipi = xen_ipis, idx = 0; idx < nitems(xen_ipis); ipi++, idx++) {
  273. if (ipi->filter == NULL) {
  274. ipi_handle[idx] = NULL;
  275. continue;
  276. }
  277. rc = xen_intr_alloc_and_bind_ipi(cpu, ipi->filter,
  278. INTR_TYPE_TTY, &ipi_handle[idx]);
  279. if (rc != 0)
  280. panic("Unable to allocate a XEN IPI port");
  281. xen_intr_describe(ipi_handle[idx], "%s", ipi->description);
  282. }
  283. }
  284. static void
  285. xen_setup_cpus(void)
  286. {
  287. uint32_t regs[4];
  288. int i;
  289. if (!xen_vector_callback_enabled)
  290. return;
  291. /*
  292. * Check whether the APIC virtualization is hardware assisted, as
  293. * that's faster than using event channels because it avoids the VM
  294. * exit.
  295. */
  296. KASSERT(hv_base != 0, ("Invalid base Xen CPUID leaf"));
  297. cpuid_count(hv_base + 4, 0, regs);
  298. if ((x2apic_mode && (regs[0] & XEN_HVM_CPUID_X2APIC_VIRT)) ||
  299. (!x2apic_mode && (regs[0] & XEN_HVM_CPUID_APIC_ACCESS_VIRT)))
  300. return;
  301. CPU_FOREACH(i)
  302. xen_cpu_ipi_init(i);
  303. /* Set the xen pv ipi ops to replace the native ones */
  304. ipi_vectored = xen_pv_lapic_ipi_vectored;
  305. native_ipi_vectored = ipi_vectored;
  306. }
  307. /* Switch to using PV IPIs as soon as the vcpu_id is set. */
  308. SYSINIT(xen_setup_cpus, SI_SUB_SMP, SI_ORDER_SECOND, xen_setup_cpus, NULL);