gunyah_vcpu.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
  4. */
  5. #include <linux/anon_inodes.h>
  6. #include <linux/file.h>
  7. #include <linux/gunyah.h>
  8. #include <linux/gunyah_vm_mgr.h>
  9. #include <linux/interrupt.h>
  10. #include <linux/kref.h>
  11. #include <linux/mm.h>
  12. #include <linux/module.h>
  13. #include <linux/types.h>
  14. #include <linux/wait.h>
  15. #include "vm_mgr.h"
  16. #include <uapi/linux/gunyah.h>
  17. #define MAX_VCPU_NAME 20 /* gh-vcpu:u32_max+NUL */
  18. struct gh_vcpu {
  19. struct gh_vm_function_instance *f;
  20. struct gh_resource *rsc;
  21. struct mutex run_lock;
  22. /* Track why vcpu_run left last time around. */
  23. enum {
  24. GH_VCPU_UNKNOWN = 0,
  25. GH_VCPU_READY,
  26. GH_VCPU_MMIO_READ,
  27. GH_VCPU_SYSTEM_DOWN,
  28. } state;
  29. u8 mmio_read_len;
  30. struct gh_vcpu_run *vcpu_run;
  31. struct completion ready;
  32. struct gh_vm *ghvm;
  33. struct notifier_block nb;
  34. struct gh_vm_resource_ticket ticket;
  35. struct kref kref;
  36. };
  37. static void vcpu_release(struct kref *kref)
  38. {
  39. struct gh_vcpu *vcpu = container_of(kref, struct gh_vcpu, kref);
  40. free_page((unsigned long)vcpu->vcpu_run);
  41. kfree(vcpu);
  42. }
  43. /*
  44. * When hypervisor allows us to schedule vCPU again, it gives us an interrupt
  45. */
  46. static irqreturn_t gh_vcpu_irq_handler(int irq, void *data)
  47. {
  48. struct gh_vcpu *vcpu = data;
  49. complete(&vcpu->ready);
  50. return IRQ_HANDLED;
  51. }
  52. static bool gh_handle_mmio(struct gh_vcpu *vcpu,
  53. struct gh_hypercall_vcpu_run_resp *vcpu_run_resp)
  54. {
  55. int ret = 0;
  56. u64 addr = vcpu_run_resp->state_data[0],
  57. len = vcpu_run_resp->state_data[1],
  58. data = vcpu_run_resp->state_data[2];
  59. if (WARN_ON(len > sizeof(u64)))
  60. len = sizeof(u64);
  61. if (vcpu_run_resp->state == GH_VCPU_ADDRSPACE_VMMIO_READ) {
  62. vcpu->vcpu_run->mmio.is_write = 0;
  63. /* Record that we need to give vCPU user's supplied value next gh_vcpu_run() */
  64. vcpu->state = GH_VCPU_MMIO_READ;
  65. vcpu->mmio_read_len = len;
  66. } else { /* GH_VCPU_ADDRSPACE_VMMIO_WRITE */
  67. /* Try internal handlers first */
  68. ret = gh_vm_mmio_write(vcpu->f->ghvm, addr, len, data);
  69. if (!ret)
  70. return true;
  71. /* Give userspace the info */
  72. vcpu->vcpu_run->mmio.is_write = 1;
  73. memcpy(vcpu->vcpu_run->mmio.data, &data, len);
  74. }
  75. vcpu->vcpu_run->mmio.phys_addr = addr;
  76. vcpu->vcpu_run->mmio.len = len;
  77. vcpu->vcpu_run->exit_reason = GH_VCPU_EXIT_MMIO;
  78. return false;
  79. }
  80. static int gh_vcpu_rm_notification(struct notifier_block *nb, unsigned long action, void *data)
  81. {
  82. struct gh_vcpu *vcpu = container_of(nb, struct gh_vcpu, nb);
  83. struct gh_rm_vm_exited_payload *exit_payload = data;
  84. if (action == GH_RM_NOTIFICATION_VM_EXITED &&
  85. le16_to_cpu(exit_payload->vmid) == vcpu->ghvm->vmid)
  86. complete(&vcpu->ready);
  87. return NOTIFY_OK;
  88. }
  89. static inline enum gh_vm_status remap_vm_status(enum gh_rm_vm_status rm_status)
  90. {
  91. switch (rm_status) {
  92. case GH_RM_VM_STATUS_INIT_FAILED:
  93. return GH_VM_STATUS_LOAD_FAILED;
  94. case GH_RM_VM_STATUS_EXITED:
  95. return GH_VM_STATUS_EXITED;
  96. default:
  97. return GH_VM_STATUS_CRASHED;
  98. }
  99. }
  100. /**
  101. * gh_vcpu_check_system() - Check whether VM as a whole is running
  102. * @vcpu: Pointer to gh_vcpu
  103. *
  104. * Returns true if the VM is alive.
  105. * Returns false if the vCPU is the VM is not alive (can only be that VM is shutting down).
  106. */
  107. static bool gh_vcpu_check_system(struct gh_vcpu *vcpu)
  108. __must_hold(&vcpu->run_lock)
  109. {
  110. bool ret = true;
  111. down_read(&vcpu->ghvm->status_lock);
  112. if (likely(vcpu->ghvm->vm_status == GH_RM_VM_STATUS_RUNNING))
  113. goto out;
  114. vcpu->vcpu_run->status.status = remap_vm_status(vcpu->ghvm->vm_status);
  115. vcpu->vcpu_run->status.exit_info = vcpu->ghvm->exit_info;
  116. vcpu->vcpu_run->exit_reason = GH_VCPU_EXIT_STATUS;
  117. vcpu->state = GH_VCPU_SYSTEM_DOWN;
  118. ret = false;
  119. out:
  120. up_read(&vcpu->ghvm->status_lock);
  121. return ret;
  122. }
  123. /**
  124. * gh_vcpu_run() - Request Gunyah to begin scheduling this vCPU.
  125. * @vcpu: The client descriptor that was obtained via gh_vcpu_alloc()
  126. */
  127. static int gh_vcpu_run(struct gh_vcpu *vcpu)
  128. {
  129. struct gh_hypercall_vcpu_run_resp vcpu_run_resp;
  130. u64 state_data[3] = { 0 };
  131. enum gh_error gh_error;
  132. int ret = 0;
  133. if (!vcpu->f)
  134. return -ENODEV;
  135. if (mutex_lock_interruptible(&vcpu->run_lock))
  136. return -ERESTARTSYS;
  137. if (!vcpu->rsc) {
  138. ret = -ENODEV;
  139. goto out;
  140. }
  141. switch (vcpu->state) {
  142. case GH_VCPU_UNKNOWN:
  143. if (vcpu->ghvm->vm_status != GH_RM_VM_STATUS_RUNNING) {
  144. /* Check if VM is up. If VM is starting, will block until VM is fully up
  145. * since that thread does down_write.
  146. */
  147. if (!gh_vcpu_check_system(vcpu))
  148. goto out;
  149. }
  150. vcpu->state = GH_VCPU_READY;
  151. break;
  152. case GH_VCPU_MMIO_READ:
  153. if (unlikely(vcpu->mmio_read_len > sizeof(state_data[0])))
  154. vcpu->mmio_read_len = sizeof(state_data[0]);
  155. memcpy(&state_data[0], vcpu->vcpu_run->mmio.data, vcpu->mmio_read_len);
  156. vcpu->state = GH_VCPU_READY;
  157. break;
  158. case GH_VCPU_SYSTEM_DOWN:
  159. goto out;
  160. default:
  161. break;
  162. }
  163. while (!ret && !signal_pending(current)) {
  164. if (vcpu->vcpu_run->immediate_exit) {
  165. ret = -EINTR;
  166. goto out;
  167. }
  168. gh_error = gh_hypercall_vcpu_run(vcpu->rsc->capid, state_data, &vcpu_run_resp);
  169. memset(state_data, 0, sizeof(state_data));
  170. if (gh_error == GH_ERROR_OK) {
  171. switch (vcpu_run_resp.state) {
  172. case GH_VCPU_STATE_READY:
  173. if (need_resched())
  174. schedule();
  175. break;
  176. case GH_VCPU_STATE_POWERED_OFF:
  177. /* vcpu might be off because the VM is shut down.
  178. * If so, it won't ever run again: exit back to user
  179. */
  180. if (!gh_vcpu_check_system(vcpu))
  181. goto out;
  182. /* Otherwise, another vcpu will turn it on (e.g. by PSCI)
  183. * and hyp sends an interrupt to wake Linux up.
  184. */
  185. fallthrough;
  186. case GH_VCPU_STATE_EXPECTS_WAKEUP:
  187. ret = wait_for_completion_interruptible(&vcpu->ready);
  188. /* reinitialize completion before next hypercall. If we reinitialize
  189. * after the hypercall, interrupt may have already come before
  190. * re-initializing the completion and then end up waiting for
  191. * event that already happened.
  192. */
  193. reinit_completion(&vcpu->ready);
  194. /* Check system status again. Completion might've
  195. * come from gh_vcpu_rm_notification
  196. */
  197. if (!ret && !gh_vcpu_check_system(vcpu))
  198. goto out;
  199. break;
  200. case GH_VCPU_STATE_BLOCKED:
  201. schedule();
  202. break;
  203. case GH_VCPU_ADDRSPACE_VMMIO_READ:
  204. case GH_VCPU_ADDRSPACE_VMMIO_WRITE:
  205. if (!gh_handle_mmio(vcpu, &vcpu_run_resp))
  206. goto out;
  207. break;
  208. default:
  209. pr_warn_ratelimited("Unknown vCPU state: %llx\n",
  210. vcpu_run_resp.sized_state);
  211. schedule();
  212. break;
  213. }
  214. } else if (gh_error == GH_ERROR_RETRY) {
  215. schedule();
  216. } else {
  217. ret = gh_error_remap(gh_error);
  218. }
  219. }
  220. out:
  221. mutex_unlock(&vcpu->run_lock);
  222. if (signal_pending(current))
  223. return -ERESTARTSYS;
  224. return ret;
  225. }
  226. static long gh_vcpu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  227. {
  228. struct gh_vcpu *vcpu = filp->private_data;
  229. long ret = -EINVAL;
  230. switch (cmd) {
  231. case GH_VCPU_RUN:
  232. ret = gh_vcpu_run(vcpu);
  233. break;
  234. case GH_VCPU_MMAP_SIZE:
  235. ret = PAGE_SIZE;
  236. break;
  237. default:
  238. break;
  239. }
  240. return ret;
  241. }
  242. static int gh_vcpu_release(struct inode *inode, struct file *filp)
  243. {
  244. struct gh_vcpu *vcpu = filp->private_data;
  245. gh_vm_put(vcpu->ghvm);
  246. kref_put(&vcpu->kref, vcpu_release);
  247. return 0;
  248. }
  249. static vm_fault_t gh_vcpu_fault(struct vm_fault *vmf)
  250. {
  251. struct gh_vcpu *vcpu = vmf->vma->vm_file->private_data;
  252. struct page *page = NULL;
  253. if (vmf->pgoff == 0)
  254. page = virt_to_page(vcpu->vcpu_run);
  255. get_page(page);
  256. vmf->page = page;
  257. return 0;
  258. }
  259. static const struct vm_operations_struct gh_vcpu_ops = {
  260. .fault = gh_vcpu_fault,
  261. };
  262. static int gh_vcpu_mmap(struct file *file, struct vm_area_struct *vma)
  263. {
  264. vma->vm_ops = &gh_vcpu_ops;
  265. return 0;
  266. }
  267. static const struct file_operations gh_vcpu_fops = {
  268. .owner = THIS_MODULE,
  269. .unlocked_ioctl = gh_vcpu_ioctl,
  270. .release = gh_vcpu_release,
  271. .llseek = noop_llseek,
  272. .mmap = gh_vcpu_mmap,
  273. };
  274. static bool gh_vcpu_populate(struct gh_vm_resource_ticket *ticket, struct gh_resource *ghrsc)
  275. {
  276. struct gh_vcpu *vcpu = container_of(ticket, struct gh_vcpu, ticket);
  277. int ret;
  278. mutex_lock(&vcpu->run_lock);
  279. if (vcpu->rsc) {
  280. pr_warn("vcpu%d already got a Gunyah resource. Check if multiple resources with same label were configured.\n",
  281. vcpu->ticket.label);
  282. ret = -EEXIST;
  283. goto out;
  284. }
  285. vcpu->rsc = ghrsc;
  286. init_completion(&vcpu->ready);
  287. ret = request_irq(vcpu->rsc->irq, gh_vcpu_irq_handler, IRQF_TRIGGER_RISING, "gh_vcpu",
  288. vcpu);
  289. if (ret)
  290. pr_warn("Failed to request vcpu irq %d: %d", vcpu->rsc->irq, ret);
  291. enable_irq_wake(vcpu->rsc->irq);
  292. out:
  293. mutex_unlock(&vcpu->run_lock);
  294. return !ret;
  295. }
  296. static void gh_vcpu_unpopulate(struct gh_vm_resource_ticket *ticket,
  297. struct gh_resource *ghrsc)
  298. {
  299. struct gh_vcpu *vcpu = container_of(ticket, struct gh_vcpu, ticket);
  300. vcpu->vcpu_run->immediate_exit = true;
  301. complete_all(&vcpu->ready);
  302. mutex_lock(&vcpu->run_lock);
  303. free_irq(vcpu->rsc->irq, vcpu);
  304. vcpu->rsc = NULL;
  305. mutex_unlock(&vcpu->run_lock);
  306. }
  307. static long gh_vcpu_bind(struct gh_vm_function_instance *f)
  308. {
  309. struct gh_fn_vcpu_arg *arg = f->argp;
  310. struct gh_vcpu *vcpu;
  311. char name[MAX_VCPU_NAME];
  312. struct file *file;
  313. struct page *page;
  314. int fd;
  315. long r;
  316. if (f->arg_size != sizeof(*arg))
  317. return -EINVAL;
  318. vcpu = kzalloc(sizeof(*vcpu), GFP_KERNEL);
  319. if (!vcpu)
  320. return -ENOMEM;
  321. vcpu->f = f;
  322. f->data = vcpu;
  323. mutex_init(&vcpu->run_lock);
  324. kref_init(&vcpu->kref);
  325. page = alloc_page(GFP_KERNEL | __GFP_ZERO);
  326. if (!page) {
  327. r = -ENOMEM;
  328. goto err_destroy_vcpu;
  329. }
  330. vcpu->vcpu_run = page_address(page);
  331. vcpu->ticket.resource_type = GH_RESOURCE_TYPE_VCPU;
  332. vcpu->ticket.label = arg->id;
  333. vcpu->ticket.owner = THIS_MODULE;
  334. vcpu->ticket.populate = gh_vcpu_populate;
  335. vcpu->ticket.unpopulate = gh_vcpu_unpopulate;
  336. r = gh_vm_add_resource_ticket(f->ghvm, &vcpu->ticket);
  337. if (r)
  338. goto err_destroy_page;
  339. if (!gh_vm_get(f->ghvm)) {
  340. r = -ENODEV;
  341. goto err_remove_resource_ticket;
  342. }
  343. vcpu->ghvm = f->ghvm;
  344. vcpu->nb.notifier_call = gh_vcpu_rm_notification;
  345. /* Ensure we run after the vm_mgr handles the notification and does
  346. * any necessary state changes. We wake up to check the new state.
  347. */
  348. vcpu->nb.priority = -1;
  349. r = gh_rm_notifier_register(f->rm, &vcpu->nb);
  350. if (r)
  351. goto err_put_gh_vm;
  352. kref_get(&vcpu->kref);
  353. fd = get_unused_fd_flags(O_CLOEXEC);
  354. if (fd < 0) {
  355. r = fd;
  356. goto err_notifier;
  357. }
  358. snprintf(name, sizeof(name), "gh-vcpu:%u", vcpu->ticket.label);
  359. file = anon_inode_getfile(name, &gh_vcpu_fops, vcpu, O_RDWR);
  360. if (IS_ERR(file)) {
  361. r = PTR_ERR(file);
  362. goto err_put_fd;
  363. }
  364. fd_install(fd, file);
  365. return fd;
  366. err_put_fd:
  367. put_unused_fd(fd);
  368. err_notifier:
  369. gh_rm_notifier_unregister(f->rm, &vcpu->nb);
  370. err_put_gh_vm:
  371. gh_vm_put(vcpu->ghvm);
  372. err_remove_resource_ticket:
  373. gh_vm_remove_resource_ticket(f->ghvm, &vcpu->ticket);
  374. err_destroy_page:
  375. free_page((unsigned long)vcpu->vcpu_run);
  376. err_destroy_vcpu:
  377. kfree(vcpu);
  378. return r;
  379. }
  380. static void gh_vcpu_unbind(struct gh_vm_function_instance *f)
  381. {
  382. struct gh_vcpu *vcpu = f->data;
  383. gh_rm_notifier_unregister(f->rm, &vcpu->nb);
  384. gh_vm_remove_resource_ticket(vcpu->f->ghvm, &vcpu->ticket);
  385. vcpu->f = NULL;
  386. kref_put(&vcpu->kref, vcpu_release);
  387. }
  388. static bool gh_vcpu_compare(const struct gh_vm_function_instance *f,
  389. const void *arg, size_t size)
  390. {
  391. const struct gh_fn_vcpu_arg *instance = f->argp,
  392. *other = arg;
  393. if (sizeof(*other) != size)
  394. return false;
  395. return instance->id == other->id;
  396. }
  397. DECLARE_GH_VM_FUNCTION_INIT(vcpu, GH_FN_VCPU, 1, gh_vcpu_bind, gh_vcpu_unbind, gh_vcpu_compare);
  398. MODULE_DESCRIPTION("Gunyah vCPU Function");
  399. MODULE_LICENSE("GPL");