diff --git a/drivers/virt/gunyah/gunyah_qtvm.c b/drivers/virt/gunyah/gunyah_qtvm.c index e8a6aa77107e..5ed23ca12818 100644 --- a/drivers/virt/gunyah/gunyah_qtvm.c +++ b/drivers/virt/gunyah/gunyah_qtvm.c @@ -4,13 +4,14 @@ */ #include +#include #include "vm_mgr.h" #define PAS_VM_METADATA_SZ 8192 static DEFINE_MUTEX(gunyah_qtvm_lock); static LIST_HEAD(gunyah_qtvm_list); -SRCU_NOTIFIER_HEAD_STATIC(gunyah_vm_notifier); +SRCU_NOTIFIER_HEAD_STATIC(gunyah_qtvm_notifier); struct gunyah_qtvm { struct gunyah_vm *ghvm; @@ -22,6 +23,24 @@ struct gunyah_qtvm { u16 vmid; }; +int gunyah_qtvm_register_notifier(struct notifier_block *nb) +{ + return srcu_notifier_chain_register(&gunyah_qtvm_notifier, nb); +} +EXPORT_SYMBOL_GPL(gunyah_qtvm_register_notifier); + +int gunyah_qtvm_unregister_notifier(struct notifier_block *nb) +{ + return srcu_notifier_chain_unregister(&gunyah_qtvm_notifier, nb); +} +EXPORT_SYMBOL_GPL(gunyah_qtvm_unregister_notifier); + +static void gunyah_notify_clients(struct gunyah_qtvm *vm, + enum gunyah_qtvm_state state) +{ + srcu_notifier_call_chain(&gunyah_qtvm_notifier, state, &vm->vmid); +} + static u16 gunyah_qtvm_pre_alloc_vmid(struct gunyah_vm *ghvm) { struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data; @@ -109,10 +128,45 @@ static int gunyah_qtvm_authenticate(struct gunyah_vm *ghvm) return 0; } +static int gunyah_qtvm_pre_vm_start(struct gunyah_vm *ghvm) +{ + struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data; + + gunyah_notify_clients(vm, GUNYAH_QTVM_BEFORE_POWERUP); + return 0; +} + +static void gunyah_qtvm_vm_start_fail(struct gunyah_vm *ghvm) +{ + struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data; + + gunyah_notify_clients(vm, GUNYAH_QTVM_POWERUP_FAIL); +} + +static int gunyah_qtvm_pre_vm_reset(struct gunyah_vm *ghvm) +{ + struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data; + + gunyah_notify_clients(vm, GUNYAH_QTVM_EXITED); + return 0; +} + +static int gunyah_qtvm_post_vm_reset(struct gunyah_vm *ghvm) +{ + struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data; + + gunyah_notify_clients(vm, GUNYAH_QTVM_EARLY_POWEROFF); + return 0; +} + static struct gunyah_auth_vm_mgr_ops vm_ops = { .pre_alloc_vmid = gunyah_qtvm_pre_alloc_vmid, .pre_vm_configure = gunyah_qtvm_pre_vm_configure, .vm_authenticate = gunyah_qtvm_authenticate, + .pre_vm_start = gunyah_qtvm_pre_vm_start, + .vm_start_fail = gunyah_qtvm_vm_start_fail, + .pre_vm_reset = gunyah_qtvm_pre_vm_reset, + .post_vm_reset = gunyah_qtvm_post_vm_reset, }; static long gunyah_qtvm_attach(struct gunyah_vm *ghvm, struct gunyah_auth_desc *desc) @@ -161,6 +215,7 @@ static void gunyah_qtvm_detach(struct gunyah_vm *ghvm) struct gunyah_qtvm *vm = ghvm->auth_vm_mgr_data; kfree(vm->parcel_list); + gunyah_notify_clients(vm, GUNYAH_QTVM_POWEROFF); list_del(&vm->list); kfree(vm); ghvm->auth_vm_mgr_ops = NULL; diff --git a/drivers/virt/gunyah/vm_mgr.c b/drivers/virt/gunyah/vm_mgr.c index 40198d74fb9a..c5c2f589a720 100644 --- a/drivers/virt/gunyah/vm_mgr.c +++ b/drivers/virt/gunyah/vm_mgr.c @@ -1127,6 +1127,10 @@ static void _gunyah_vm_put(struct kref *kref) gunyah_vm_remove_resource_ticket(ghvm, &ghvm->guest_private_extent_ticket); /* clang-format on */ + ret = gunyah_vm_pre_vm_reset(ghvm); + if (ret) + dev_err(ghvm->parent, "Failed pre reset the vm: %d\n", ret); + gunyah_vm_clean_resources(ghvm); if (ghvm->vm_status == GUNYAH_RM_VM_STATUS_EXITED || @@ -1138,7 +1142,11 @@ static void _gunyah_vm_put(struct kref *kref) wait_event(ghvm->vm_status_wait, ghvm->vm_status == GUNYAH_RM_VM_STATUS_RESET); else - dev_err(ghvm->parent, "Failed to reset the vm: %d\n",ret); + dev_err(ghvm->parent, "Failed to reset the vm: %d\n", ret); + + ret = gunyah_vm_post_vm_reset(ghvm); + if (ret) + dev_err(ghvm->parent, "Failed post reset the vm: %d\n", ret); /* clang-format on */ } diff --git a/drivers/virt/gunyah/vm_mgr.h b/drivers/virt/gunyah/vm_mgr.h index e360f5bef5cb..6919d36d4682 100644 --- a/drivers/virt/gunyah/vm_mgr.h +++ b/drivers/virt/gunyah/vm_mgr.h @@ -329,4 +329,19 @@ static inline void gunyah_vm_start_fail(struct gunyah_vm *ghvm) return ghvm->auth_vm_mgr_ops->vm_start_fail(ghvm); } +static inline int gunyah_vm_pre_vm_reset(struct gunyah_vm *ghvm) +{ + if (ghvm->auth_vm_mgr_ops->pre_vm_reset) + return ghvm->auth_vm_mgr_ops->pre_vm_reset(ghvm); + + return 0; +} + +static inline int gunyah_vm_post_vm_reset(struct gunyah_vm *ghvm) +{ + if (ghvm->auth_vm_mgr_ops->post_vm_reset) + return ghvm->auth_vm_mgr_ops->post_vm_reset(ghvm); + + return 0; +} #endif diff --git a/include/linux/gunyah.h b/include/linux/gunyah.h index 5717b0710c45..f58ed64b980a 100644 --- a/include/linux/gunyah.h +++ b/include/linux/gunyah.h @@ -37,6 +37,9 @@ void gunyah_vm_put(struct gunyah_vm *ghvm); * choose to setup if you want demand paged VM or not. (Optional) * @pre_vm_start: Callback for any setup before VM start where client * drivers can share/lend memory. (Optional) + * @pre_vm_reset: Callback for any cleanup before VM reset. All resources + * tracked by RM will be cleaned at this stage (Optional) + * @post_vm_reset: Callback for any cleanup after VM reset (Optional) * @start_fail: Needed when roll back is needed before auth_mgr can * clean up at a later stage. **/ @@ -46,6 +49,8 @@ struct gunyah_auth_vm_mgr_ops { int (*vm_authenticate)(struct gunyah_vm *ghvm); int (*pre_vm_init)(struct gunyah_vm *ghvm); int (*pre_vm_start)(struct gunyah_vm *ghvm); + int (*pre_vm_reset)(struct gunyah_vm *ghvm); + int (*post_vm_reset)(struct gunyah_vm *ghvm); void (*vm_start_fail)(struct gunyah_vm *ghvm); }; diff --git a/include/linux/gunyah_qtvm.h b/include/linux/gunyah_qtvm.h new file mode 100644 index 000000000000..3986b207a395 --- /dev/null +++ b/include/linux/gunyah_qtvm.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _LINUX_GUNYAH_QTVM_H +#define _LINUX_GUNYAH_QTVM_H + +#include +#include + +enum gunyah_qtvm_state { + GUNYAH_QTVM_BEFORE_POWERUP = 1, + GUNYAH_QTVM_POWERUP_FAIL = 2, + GUNYAH_QTVM_EARLY_POWEROFF = 3, + GUNYAH_QTVM_POWEROFF = 4, + GUNYAH_QTVM_EXITED = 5, + GUNYAH_QTVM_CRASH = 6, +}; + +#if IS_ENABLED(CONFIG_GUNYAH_QCOM_TRUSTED_VM) +int gunyah_qtvm_register_notifier(struct notifier_block *nb); +int gunyah_qtvm_unregister_notifier(struct notifier_block *nb); +int gunyah_qtvm_register_resource_ticket(struct gunyah_vm_resource_ticket *t, + u16 vmid); +void gunyah_qtvm_unregister_resource_ticket(struct gunyah_vm_resource_ticket *t, + u16 vmid); +#else +static inline int gunyah_qtvm_register_notifier(struct notifier_block *nb) +{ + return 0; +} +static inline int gunyah_qtvm_unregister_notifier(struct notifier_block *nb) +{ + return 0; +} +static inline int gunyah_qtvm_register_resource_ticket( + struct gunyah_vm_resource_ticket *t, u16 vmid) +{ + return 0; +} +static inline void gunyah_qtvm_unregister_resource_ticket( + struct gunyah_vm_resource_ticket *t, u16 vmid) +{ +} +#endif + +#endif