diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c index cf38fd5a7766..f49dc583ea66 100644 --- a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c +++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c @@ -296,7 +296,9 @@ static int fts_ts_vm_handle_vm_hardware(struct fts_ts_data *fts_data) return rc; } -static void fts_ts_vm_irq_on_lend_callback(void *data, enum hh_irq_label label) +static void fts_ts_vm_irq_on_lend_callback(void *data, + unsigned long notif_type, + enum hh_irq_label label) { struct fts_ts_data *fts_data = data; struct irq_data *irq_data; @@ -472,6 +474,10 @@ static void fts_ts_trusted_touch_vm_mode_disable(struct fts_ts_data *fts_data) pr_err("Failed to release irq rc:%d\n", rc); else atomic_set(&fts_data->vm_info->tvm_owns_irq, 0); + + rc = hh_irq_release_notify(fts_data->vm_info->irq_label); + if (rc) + pr_err("Failed to notify release irq rc:%d\n", rc); } atomic_set(&fts_data->trusted_touch_enabled, 0); reinit_completion(&fts_data->resource_checkpoint); @@ -600,6 +606,7 @@ static void fts_ts_trusted_touch_complete(struct fts_ts_data *fts_data) } static void fts_ts_vm_irq_on_release_callback(void *data, + unsigned long notif_type, enum hh_irq_label label) { struct fts_ts_data *fts_data = data; @@ -730,7 +737,7 @@ static int fts_ts_trusted_touch_vm_mode_enable(struct fts_ts_data *fts_data) } atomic_set(&vm_info->pvm_owns_iomem, 0); - rc = hh_irq_lend(vm_info->irq_label, vm_info->vm_name, + rc = hh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name, fts_data->irq, &fts_ts_vm_irq_on_release_callback, fts_data); if (rc) { pr_err("Failed to lend irq\n"); @@ -738,6 +745,12 @@ static int fts_ts_trusted_touch_vm_mode_enable(struct fts_ts_data *fts_data) } atomic_set(&vm_info->pvm_owns_irq, 0); + rc = hh_irq_lend_notify(vm_info->irq_label); + if (rc) { + pr_err("Failed to notify irq\n"); + return -EINVAL; + } + reinit_completion(&fts_data->trusted_touch_powerdown); atomic_set(&fts_data->trusted_touch_enabled, 1); pr_debug("trusted touch enabled\n"); @@ -809,7 +822,7 @@ static int fts_ts_vm_init(struct fts_ts_data *fts_data) goto init_fail; } vm_info->mem_cookie = mem_cookie; - rc = hh_irq_wait_for_lend(vm_info->irq_label, HH_PRIMARY_VM, + rc = hh_irq_wait_for_lend_v2(vm_info->irq_label, HH_PRIMARY_VM, &fts_ts_vm_irq_on_lend_callback, fts_data); atomic_set(&vm_info->tvm_owns_irq, 0); atomic_set(&vm_info->tvm_owns_iomem, 0); diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c index 3832d89422bf..21c12f74fb99 100644 --- a/drivers/input/touchscreen/st/fts.c +++ b/drivers/input/touchscreen/st/fts.c @@ -321,7 +321,9 @@ static int fts_vm_handle_vm_hardware(struct fts_ts_info *info) return rc; } -static void fts_vm_irq_on_lend_callback(void *data, enum hh_irq_label label) +static void fts_vm_irq_on_lend_callback(void *data, + unsigned long notif_type, + enum hh_irq_label label) { struct fts_ts_info *info = data; struct irq_data *irq_data; @@ -497,6 +499,10 @@ static void fts_trusted_touch_vm_mode_disable(struct fts_ts_info *info) pr_err("Failed to release irq rc:%d\n", rc); else atomic_set(&info->vm_info->tvm_owns_irq, 0); + + rc = hh_irq_release_notify(info->vm_info->irq_label); + if (rc) + pr_err("Failed to notify release irq rc:%d\n", rc); } atomic_set(&info->trusted_touch_enabled, 0); reinit_completion(&info->resource_checkpoint); @@ -624,7 +630,9 @@ static void fts_trusted_touch_complete(struct fts_ts_info *info) } } -static void fts_vm_irq_on_release_callback(void *data, enum hh_irq_label label) +static void fts_vm_irq_on_release_callback(void *data, + unsigned long notif_type, + enum hh_irq_label label) { struct fts_ts_info *info = data; int rc = 0; @@ -755,7 +763,7 @@ static int fts_trusted_touch_vm_mode_enable(struct fts_ts_info *info) } atomic_set(&vm_info->pvm_owns_iomem, 0); - rc = hh_irq_lend(vm_info->irq_label, vm_info->vm_name, + rc = hh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name, info->client->irq, &fts_vm_irq_on_release_callback, info); if (rc) { pr_err("Failed to lend irq\n"); @@ -763,6 +771,12 @@ static int fts_trusted_touch_vm_mode_enable(struct fts_ts_info *info) } atomic_set(&vm_info->pvm_owns_irq, 0); + rc = hh_irq_lend_notify(vm_info->irq_label); + if (rc) { + pr_err("Failed to notify irq\n"); + return -EINVAL; + } + reinit_completion(&info->trusted_touch_powerdown); atomic_set(&info->trusted_touch_enabled, 1); pr_debug("trusted touch enabled\n"); @@ -833,7 +847,7 @@ static int fts_vm_init(struct fts_ts_info *info) goto init_fail; } vm_info->mem_cookie = mem_cookie; - rc = hh_irq_wait_for_lend(vm_info->irq_label, HH_PRIMARY_VM, + rc = hh_irq_wait_for_lend_v2(vm_info->irq_label, HH_PRIMARY_VM, &fts_vm_irq_on_lend_callback, info); atomic_set(&vm_info->tvm_owns_irq, 0); atomic_set(&vm_info->tvm_owns_iomem, 0); diff --git a/drivers/virt/haven/hh_irq_lend.c b/drivers/virt/haven/hh_irq_lend.c index 76fd1f7fdc58..9e4a7fb4d354 100644 --- a/drivers/virt/haven/hh_irq_lend.c +++ b/drivers/virt/haven/hh_irq_lend.c @@ -17,22 +17,20 @@ struct hh_irq_entry { hh_vmid_t vmid; enum hh_vm_names vm_name; + hh_irq_handle_fn_v2 v2_handle; hh_irq_handle_fn handle; void *data; enum { - /* start state */ HH_IRQ_STATE_NONE, - /* NONE -> WAIT_RELEASE by hh_irq_lend */ - HH_IRQ_STATE_WAIT_RELEASE, - /* NONE -> WAIT_LEND by hh_irq_wait_lend */ + + HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT, HH_IRQ_STATE_WAIT_LEND, - /* WAIT_RELEASE -> RELEASED by notifier */ - /* RELEASED -> NONE by hh_irq_reclaim */ - HH_IRQ_STATE_RELEASED, - /* WAIT_LEND -> LENT by notifier */ - /* LENT -> NONE by hh_irq_release */ - HH_IRQ_STATE_LENT, + + /* notification states */ + HH_IRQ_STATE_RELEASED, /* svm -> pvm */ + HH_IRQ_STATE_ACCEPTED, /* svm -> pvm */ + HH_IRQ_STATE_LENT, /* pvm -> svm */ } state; hh_virq_handle_t virq_handle; }; @@ -40,31 +38,50 @@ struct hh_irq_entry { static struct hh_irq_entry hh_irq_entries[HH_IRQ_LABEL_MAX]; static DEFINE_SPINLOCK(hh_irq_lend_lock); -static int hh_irq_released_nb_handler(struct notifier_block *this, +static int hh_irq_released_accepted_nb_handler(struct notifier_block *this, unsigned long cmd, void *data) { unsigned long flags; enum hh_irq_label label; struct hh_irq_entry *entry; - struct hh_rm_notif_vm_irq_released_payload *released = data; + struct hh_rm_notif_vm_irq_released_payload *released; + struct hh_rm_notif_vm_irq_accepted_payload *accepted; - if (cmd != HH_RM_NOTIF_VM_IRQ_RELEASED) + if (cmd != HH_RM_NOTIF_VM_IRQ_RELEASED && + cmd != HH_RM_NOTIF_VM_IRQ_ACCEPTED) return NOTIFY_DONE; spin_lock_irqsave(&hh_irq_lend_lock, flags); for (label = 0; label < HH_IRQ_LABEL_MAX; label++) { entry = &hh_irq_entries[label]; - if (entry->state != HH_IRQ_STATE_WAIT_RELEASE) + + if (entry->state != HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT && + entry->state != HH_IRQ_STATE_ACCEPTED) continue; - if (released->virq_handle == entry->virq_handle) { - entry->state = HH_IRQ_STATE_RELEASED; - spin_unlock_irqrestore(&hh_irq_lend_lock, - flags); + switch (cmd) { + case HH_RM_NOTIF_VM_IRQ_RELEASED: + released = data; + if (released->virq_handle == entry->virq_handle) { + entry->state = HH_IRQ_STATE_RELEASED; + spin_unlock_irqrestore(&hh_irq_lend_lock, + flags); + entry->v2_handle(entry->data, cmd, label); + return NOTIFY_OK; + } - entry->handle(entry->data, label); + break; + case HH_RM_NOTIF_VM_IRQ_ACCEPTED: + accepted = data; + if (accepted->virq_handle == entry->virq_handle) { + entry->state = HH_IRQ_STATE_ACCEPTED; + spin_unlock_irqrestore(&hh_irq_lend_lock, + flags); + entry->v2_handle(entry->data, cmd, label); + return NOTIFY_OK; + } - return NOTIFY_OK; + break; } } spin_unlock_irqrestore(&hh_irq_lend_lock, flags); @@ -72,8 +89,8 @@ static int hh_irq_released_nb_handler(struct notifier_block *this, return NOTIFY_DONE; } -static struct notifier_block hh_irq_released_nb = { - .notifier_call = hh_irq_released_nb_handler, +static struct notifier_block hh_irq_released_accepted_nb = { + .notifier_call = hh_irq_released_accepted_nb_handler, }; static int hh_irq_lent_nb_handler(struct notifier_block *this, @@ -112,7 +129,7 @@ static int hh_irq_lent_nb_handler(struct notifier_block *this, spin_unlock_irqrestore(&hh_irq_lend_lock, flags); - entry->handle(entry->data, label); + entry->v2_handle(entry->data, cmd, label); return NOTIFY_OK; } @@ -127,22 +144,24 @@ static struct notifier_block hh_irq_lent_nb = { }; /** - * hh_irq_lend: Lend a hardware interrupt to another VM + * hh_irq_lend_v2: Lend a hardware interrupt to another VM * @label: vIRQ high-level label * @name: VM name to send interrupt to * @irq: Linux IRQ number to lend - * @on_release: callback to invoke when other VM returns the - * interrupt - * @data: Argument to pass to on_release + * @cb_handle: callback to invoke when other VM release or accept the interrupt + * @data: Argument to pass to cb_handle + * + * Returns 0 on success also the handle corresponding to Linux IRQ#. + * Returns < 0 on error */ -int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name, - int irq, hh_irq_handle_fn on_release, void *data) +int hh_irq_lend_v2(enum hh_irq_label label, enum hh_vm_names name, + int irq, hh_irq_handle_fn_v2 cb_handle, void *data) { int ret, virq; unsigned long flags; struct hh_irq_entry *entry; - if (label >= HH_IRQ_LABEL_MAX || !on_release) + if (label >= HH_IRQ_LABEL_MAX || !cb_handle) return -EINVAL; entry = &hh_irq_entries[label]; @@ -163,16 +182,62 @@ int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name, return ret; } - entry->handle = on_release; + entry->v2_handle = cb_handle; entry->data = data; - entry->state = HH_IRQ_STATE_WAIT_RELEASE; + entry->state = HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT; spin_unlock_irqrestore(&hh_irq_lend_lock, flags); - return hh_rm_vm_irq_lend_notify(entry->vmid, virq, label, - &entry->virq_handle); + return hh_rm_vm_irq_lend(entry->vmid, virq, label, &entry->virq_handle); +} +EXPORT_SYMBOL(hh_irq_lend_v2); + +/** + * hh_irq_lend: Lend a hardware interrupt to another VM + * @label: vIRQ high-level label + * @name: VM name to send interrupt to + * @irq: Linux IRQ number to lend + * @cb_handle: callback to invoke when other VM release or accept the interrupt + * @data: Argument to pass to cb_handle + * + * Returns 0 on success also the handle corresponding to Linux IRQ#. + * Returns < 0 on error + */ +int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name, + int irq, hh_irq_handle_fn cb_handle, void *data) +{ + struct hh_irq_entry *entry; + + if (label >= HH_IRQ_LABEL_MAX || !cb_handle) + return -EINVAL; + + entry = &hh_irq_entries[label]; + entry->handle = cb_handle; + + return 0; } EXPORT_SYMBOL(hh_irq_lend); +/** + * hh_irq_lend_notify: Pass the irq handle to other VM for accept + * @label: vIRQ high-level label + * + * Returns 0 on success, < 0 on error + */ +int hh_irq_lend_notify(enum hh_irq_label label) +{ + struct hh_irq_entry *entry; + + if (label >= HH_IRQ_LABEL_MAX) + return -EINVAL; + + entry = &hh_irq_entries[label]; + if (entry->state == HH_IRQ_STATE_NONE) + return -EINVAL; + + return hh_rm_vm_irq_lend_notify(entry->vmid, entry->virq_handle); +} +EXPORT_SYMBOL(hh_irq_lend_notify); + /** * hh_irq_reclaim: Reclaim a hardware interrupt after other VM * has released. @@ -193,7 +258,8 @@ int hh_irq_reclaim(enum hh_irq_label label) entry = &hh_irq_entries[label]; - if (entry->state != HH_IRQ_STATE_RELEASED) + if (entry->state != HH_IRQ_STATE_WAIT_RELEASE_OR_ACCEPT && + (entry->state != HH_IRQ_STATE_RELEASED)) return -EINVAL; ret = hh_rm_vm_irq_reclaim(entry->virq_handle); @@ -204,15 +270,14 @@ int hh_irq_reclaim(enum hh_irq_label label) EXPORT_SYMBOL(hh_irq_reclaim); /** - * hh_irq_wait_lend: Register to claim a lent interrupt from another - * VM + * hh_irq_wait_for_lend_v2: Register to claim a lent interrupt from another VM * @label: vIRQ high-level label * @name: Lender's VM name. If don't care, then use HH_VM_MAX * @on_lend: callback to invoke when other VM lends the interrupt * @data: Argument to pass to on_lend */ -int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name, - hh_irq_handle_fn on_lend, void *data) +int hh_irq_wait_for_lend_v2(enum hh_irq_label label, enum hh_vm_names name, + hh_irq_handle_fn_v2 on_lend, void *data) { unsigned long flags; struct hh_irq_entry *entry; @@ -229,13 +294,27 @@ int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name, } entry->vm_name = name; - entry->handle = on_lend; + entry->v2_handle = on_lend; entry->data = data; entry->state = HH_IRQ_STATE_WAIT_LEND; spin_unlock_irqrestore(&hh_irq_lend_lock, flags); return 0; } +EXPORT_SYMBOL(hh_irq_wait_for_lend_v2); + +/** + * hh_irq_wait_lend: Register to claim a lent interrupt from another VM + * @label: vIRQ high-level label + * @name: Lender's VM name. If don't care, then use HH_VM_MAX + * @on_lend: callback to invoke when other VM lends the interrupt + * @data: Argument to pass to on_lend + */ +int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name, + hh_irq_handle_fn on_lend, void *data) +{ + return 0; +} EXPORT_SYMBOL(hh_irq_wait_for_lend); /** @@ -276,10 +355,38 @@ int hh_irq_accept(enum hh_irq_label label, int irq, int type) if (irq == -1) irq = hh_rm_virq_to_irq(virq, type); + entry->state = HH_IRQ_STATE_ACCEPTED; return irq; } EXPORT_SYMBOL(hh_irq_accept); +/** + * hh_irq_accept_notify: Notify the lend vm (pvm) that IRQ is accepted + * @label: vIRQ high-level label + * @irq: Linux IRQ# to associate vIRQ with. If don't care, use -1 + * + * Returns the Linux IRQ# that vIRQ was registered to on success. + * Returns <0 on error + * This function is not thread-safe w.r.t. IRQ lend state. Do not race with + * hh_irq_release or another hh_irq_accept with same label. + */ +int hh_irq_accept_notify(enum hh_irq_label label) +{ + struct hh_irq_entry *entry; + + if (label >= HH_IRQ_LABEL_MAX) + return -EINVAL; + + entry = &hh_irq_entries[label]; + + if (entry->state != HH_IRQ_STATE_ACCEPTED) + return -EINVAL; + + return hh_rm_vm_irq_accept_notify(entry->vmid, + entry->virq_handle); +} +EXPORT_SYMBOL(hh_irq_accept_notify); + /** * hh_irq_release: Release a lent interrupt * @label: vIRQ high-level label @@ -296,17 +403,34 @@ int hh_irq_release(enum hh_irq_label label) entry = &hh_irq_entries[label]; - if (entry->state != HH_IRQ_STATE_LENT) + if (entry->state != HH_IRQ_STATE_ACCEPTED) return -EINVAL; - ret = hh_rm_vm_irq_release_notify(entry->vmid, - entry->virq_handle); + ret = hh_rm_vm_irq_release(entry->virq_handle); if (!ret) entry->state = HH_IRQ_STATE_WAIT_LEND; return ret; } EXPORT_SYMBOL(hh_irq_release); +int hh_irq_release_notify(enum hh_irq_label label) +{ + struct hh_irq_entry *entry; + + if (label >= HH_IRQ_LABEL_MAX) + return -EINVAL; + + entry = &hh_irq_entries[label]; + + if (entry->state != HH_IRQ_STATE_ACCEPTED && + entry->state != HH_IRQ_STATE_WAIT_LEND) + return -EINVAL; + + return hh_rm_vm_irq_release_notify(entry->vmid, + entry->virq_handle); +} +EXPORT_SYMBOL(hh_irq_release_notify); + static int __init hh_irq_lend_init(void) { int ret; @@ -314,14 +438,15 @@ static int __init hh_irq_lend_init(void) ret = hh_rm_register_notifier(&hh_irq_lent_nb); if (ret) return ret; - return hh_rm_register_notifier(&hh_irq_released_nb); + + return hh_rm_register_notifier(&hh_irq_released_accepted_nb); } module_init(hh_irq_lend_init); static void hh_irq_lend_exit(void) { hh_rm_unregister_notifier(&hh_irq_lent_nb); - hh_rm_unregister_notifier(&hh_irq_released_nb); + hh_rm_unregister_notifier(&hh_irq_released_accepted_nb); } module_exit(hh_irq_lend_exit); diff --git a/drivers/virt/haven/hh_rm_core.c b/drivers/virt/haven/hh_rm_core.c index 152f9d78dbb8..67238a2819e0 100644 --- a/drivers/virt/haven/hh_rm_core.c +++ b/drivers/virt/haven/hh_rm_core.c @@ -218,6 +218,15 @@ static int hh_rm_process_notif(void *recv_buff, size_t recv_buff_size) goto err; } break; + case HH_RM_NOTIF_VM_IRQ_ACCEPTED: + if (recv_buff_size != sizeof(*hdr) + + sizeof(struct hh_rm_notif_vm_irq_accepted_payload)) { + pr_err("%s: Invalid size for VM_IRQ_ACCEPTED notif: %u\n", + __func__, recv_buff_size - sizeof(*hdr)); + ret = -EINVAL; + goto err; + } + break; case HH_RM_NOTIF_MEM_SHARED: if (recv_buff_size < sizeof(*hdr) + sizeof(struct hh_rm_notif_mem_shared_payload)) { diff --git a/drivers/virt/haven/hh_rm_drv_private.h b/drivers/virt/haven/hh_rm_drv_private.h index 36f990b24189..688e301b5779 100644 --- a/drivers/virt/haven/hh_rm_drv_private.h +++ b/drivers/virt/haven/hh_rm_drv_private.h @@ -181,6 +181,7 @@ struct hh_vm_irq_lend_resp_payload { /* Call: VM_IRQ_NOTIFY */ #define HH_VM_IRQ_NOTIFY_FLAGS_LENT BIT(0) #define HH_VM_IRQ_NOTIFY_FLAGS_RELEASED BIT(1) +#define HH_VM_IRQ_NOTIFY_FLAGS_ACCEPTED BIT(2) /* Call: VM_IRQ_RELEASE */ struct hh_vm_irq_release_req_payload { diff --git a/drivers/virt/haven/hh_rm_iface.c b/drivers/virt/haven/hh_rm_iface.c index ddcf75c384aa..1c4923c0288c 100644 --- a/drivers/virt/haven/hh_rm_iface.c +++ b/drivers/virt/haven/hh_rm_iface.c @@ -145,102 +145,6 @@ out: return resp_entries; } -/** - * hh_rm_vm_irq_accept: Bind the virq number to the supplied virq_handle - * @virq_handle: The virtual IRQ handle (for example, obtained via - * call to hh_rm_get_hyp_resources()) - * @virq: The virtual IRQ number to bind to. Note that this is the virtual - * GIC IRQ number and not the linux IRQ number. Pass -1 here if the - * caller wants the Resource Manager VM to allocate a number - * - * If provided -1 for virq, the function returns the new IRQ number, else - * the one that was already provided. - * - * The function encodes the error codes via ERR_PTR. Hence, the caller is - * responsible to check it with IS_ERR_OR_NULL(). - */ -int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq) -{ - struct hh_vm_irq_accept_resp_payload *resp_payload; - struct hh_vm_irq_accept_req_payload req_payload = {0}; - size_t resp_payload_size; - int ret, reply_err_code; - - /* -1 is valid for virq if requesting for a new number */ - if (virq < -1) - return -EINVAL; - - req_payload.virq_handle = virq_handle; - req_payload.virq = virq; - - resp_payload = hh_rm_call(HH_RM_RPC_MSG_ID_CALL_VM_IRQ_ACCEPT, - &req_payload, sizeof(req_payload), - &resp_payload_size, &reply_err_code); - if (reply_err_code || IS_ERR_OR_NULL(resp_payload)) { - ret = PTR_ERR(resp_payload); - pr_err("%s: VM_IRQ_ACCEPT failed with err: %d\n", - __func__, ret); - return ret; - } - - if (virq == -1 && resp_payload_size != sizeof(*resp_payload)) { - pr_err("%s: Invalid size received for VM_IRQ_ACCEPT: %u\n", - __func__, resp_payload_size); - ret = -EINVAL; - goto out; - } - - ret = virq == -1 ? resp_payload->virq : virq; -out: - kfree(resp_payload); - return ret; -} -EXPORT_SYMBOL(hh_rm_vm_irq_accept); - -/** - * hh_rm_vm_irq_lend: Lend an IRQ to another VM - * @vmid: VM to lend the interrupt to - * @virq: Virtual IRQ number to lend - * @label: Label to give to VM so it may know how to associate the interrupt - * @virq_handle: Response handle which RM will accept from the other VM to take - * the lent interrupt - */ -static int hh_rm_vm_irq_lend(hh_vmid_t vmid, int virq, int label, - hh_virq_handle_t *virq_handle) -{ - struct hh_vm_irq_lend_resp_payload *resp_payload; - struct hh_vm_irq_lend_req_payload req_payload = {0}; - size_t resp_payload_size; - int ret = 0, reply_err_code; - - req_payload.vmid = vmid; - req_payload.virq = virq; - req_payload.label = label; - - resp_payload = hh_rm_call(HH_RM_RPC_MSG_ID_CALL_VM_IRQ_LEND, - &req_payload, sizeof(req_payload), - &resp_payload_size, &reply_err_code); - if (reply_err_code || IS_ERR_OR_NULL(resp_payload)) { - ret = PTR_ERR(resp_payload); - pr_err("%s: VM_IRQ_LEND failed with err: %d\n", - __func__, ret); - return ret; - } - - if (resp_payload_size != sizeof(*resp_payload)) { - pr_err("%s: Invalid size received for VM_IRQ_LEND: %u\n", - __func__, resp_payload_size); - ret = -EINVAL; - goto out; - } - - if (virq_handle) - *virq_handle = resp_payload->virq; -out: - kfree(resp_payload); - return ret; -} - /** * hh_rm_vm_irq_notify: Notify an IRQ to another VM * @vmids: VMs to notify the handle about @@ -250,7 +154,7 @@ out: * the lent interrupt */ static int hh_rm_vm_irq_notify(const hh_vmid_t *vmids, unsigned int num_vmids, - u16 flags, hh_virq_handle_t virq_handle) + u16 flags, hh_virq_handle_t virq_handle) { void *resp; struct hh_vm_irq_notify_req_payload *req_payload; @@ -308,6 +212,51 @@ static int hh_rm_vm_irq_notify(const hh_vmid_t *vmids, unsigned int num_vmids, return ret; } +/** + * hh_rm_vm_irq_lend: Lend an IRQ to another VM + * @vmid: VM to lend the interrupt to + * @virq: Virtual IRQ number to lend + * @label: Label to give to VM so it may know how to associate the interrupt + * @virq_handle: Response handle which RM will accept from the other VM to take + * the lent interrupt + */ +int hh_rm_vm_irq_lend(hh_vmid_t vmid, int virq, int label, + hh_virq_handle_t *virq_handle) +{ + struct hh_vm_irq_lend_resp_payload *resp_payload; + struct hh_vm_irq_lend_req_payload req_payload = {0}; + size_t resp_payload_size; + int ret = 0, reply_err_code; + + req_payload.vmid = vmid; + req_payload.virq = virq; + req_payload.label = label; + + resp_payload = hh_rm_call(HH_RM_RPC_MSG_ID_CALL_VM_IRQ_LEND, + &req_payload, sizeof(req_payload), + &resp_payload_size, &reply_err_code); + if (reply_err_code || IS_ERR_OR_NULL(resp_payload)) { + ret = PTR_ERR(resp_payload); + pr_err("%s: VM_IRQ_LEND failed with err: %d\n", + __func__, ret); + return ret; + } + + if (resp_payload_size != sizeof(*resp_payload)) { + pr_err("%s: Invalid size received for VM_IRQ_LEND: %u\n", + __func__, resp_payload_size); + ret = -EINVAL; + goto out; + } + + if (virq_handle) + *virq_handle = resp_payload->virq; +out: + kfree(resp_payload); + return ret; +} +EXPORT_SYMBOL(hh_rm_vm_irq_lend); + /** * hh_rm_vm_irq_lend_notify: Lend an IRQ to a VM and notify the VM about it * @vmid: VM to lend interrupt to @@ -320,20 +269,10 @@ static int hh_rm_vm_irq_notify(const hh_vmid_t *vmids, unsigned int num_vmids, * This function performs interrupt sharing flow for "HLOS" described in * Resource Manager High Level Design Sec. 3.3.3. */ -int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, int virq, int label, - hh_virq_handle_t *virq_handle) +int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle) { - int ret; - - if (!virq_handle) - return -EINVAL; - - ret = hh_rm_vm_irq_lend(vmid, virq, label, virq_handle); - if (ret) - return ret; - return hh_rm_vm_irq_notify(&vmid, 1, HH_VM_IRQ_NOTIFY_FLAGS_LENT, - *virq_handle); + virq_handle); } EXPORT_SYMBOL(hh_rm_vm_irq_lend_notify); @@ -341,7 +280,7 @@ EXPORT_SYMBOL(hh_rm_vm_irq_lend_notify); * hh_rm_vm_irq_release: Return a lent IRQ * @virq_handle: IRQ handle to be released */ -static int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) +int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) { struct hh_vm_irq_release_req_payload req_payload = {0}; void *resp; @@ -374,6 +313,7 @@ static int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) return ret; } +EXPORT_SYMBOL(hh_rm_vm_irq_release); /** * hh_rm_vm_irq_release_notify: Release IRQ back to a VM and notify that it has @@ -383,17 +323,76 @@ static int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) */ int hh_rm_vm_irq_release_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle) { - int ret; - - ret = hh_rm_vm_irq_release(virq_handle); - if (ret) - return ret; - return hh_rm_vm_irq_notify(NULL, 0, HH_VM_IRQ_NOTIFY_FLAGS_RELEASED, virq_handle); } EXPORT_SYMBOL(hh_rm_vm_irq_release_notify); +/** + * hh_rm_vm_irq_accept: Bind the virq number to the supplied virq_handle + * @virq_handle: The virtual IRQ handle (for example, obtained via + * call to hh_rm_get_hyp_resources()) + * @virq: The virtual IRQ number to bind to. Note that this is the virtual + * GIC IRQ number and not the linux IRQ number. Pass -1 here if the + * caller wants the Resource Manager VM to allocate a number + * + * If provided -1 for virq, the function returns the new IRQ number, else + * the one that was already provided. + * + * The function encodes the error codes via ERR_PTR. Hence, the caller is + * responsible to check it with IS_ERR_OR_NULL(). + */ +int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq) +{ + struct hh_vm_irq_accept_resp_payload *resp_payload; + struct hh_vm_irq_accept_req_payload req_payload = {0}; + size_t resp_payload_size; + int ret, reply_err_code; + + /* -1 is valid for virq if requesting for a new number */ + if (virq < -1) + return -EINVAL; + + req_payload.virq_handle = virq_handle; + req_payload.virq = virq; + + resp_payload = hh_rm_call(HH_RM_RPC_MSG_ID_CALL_VM_IRQ_ACCEPT, + &req_payload, sizeof(req_payload), + &resp_payload_size, &reply_err_code); + if (reply_err_code || IS_ERR_OR_NULL(resp_payload)) { + ret = PTR_ERR(resp_payload); + pr_err("%s: VM_IRQ_ACCEPT failed with err: %d\n", + __func__, ret); + return ret; + } + + if (virq == -1 && resp_payload_size != sizeof(*resp_payload)) { + pr_err("%s: Invalid size received for VM_IRQ_ACCEPT: %u\n", + __func__, resp_payload_size); + ret = -EINVAL; + goto out; + } + + ret = virq == -1 ? resp_payload->virq : virq; +out: + kfree(resp_payload); + return ret; +} +EXPORT_SYMBOL(hh_rm_vm_irq_accept); + +/** + * hh_rm_vm_irq_release_notify: Release IRQ back to a VM and notify that it has + * been released. + * @vmid: VM to release interrupt to + * @virq_handle: Virtual IRQ handle to release + */ +int hh_rm_vm_irq_accept_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle) +{ + return hh_rm_vm_irq_notify(NULL, 0, HH_VM_IRQ_NOTIFY_FLAGS_ACCEPTED, + virq_handle); +} +EXPORT_SYMBOL(hh_rm_vm_irq_accept_notify); + /** * hh_rm_vm_irq_reclaim: Return a lent IRQ * @virq_handle: IRQ handle to be reclaimed diff --git a/include/linux/haven/hh_irq_lend.h b/include/linux/haven/hh_irq_lend.h index f2737395b28d..afed74599cc9 100644 --- a/include/linux/haven/hh_irq_lend.h +++ b/include/linux/haven/hh_irq_lend.h @@ -19,14 +19,21 @@ enum hh_irq_label { }; typedef void (*hh_irq_handle_fn)(void *req, enum hh_irq_label label); - +typedef void (*hh_irq_handle_fn_v2)(void *req, unsigned long notif_type, + enum hh_irq_label label); int hh_irq_lend(enum hh_irq_label label, enum hh_vm_names name, - int hw_irq, hh_irq_handle_fn on_release, void *data); + int hw_irq, hh_irq_handle_fn cb_handle, void *data); +int hh_irq_lend_v2(enum hh_irq_label label, enum hh_vm_names name, + int hw_irq, hh_irq_handle_fn_v2 cb_handle, void *data); +int hh_irq_lend_notify(enum hh_irq_label label); int hh_irq_reclaim(enum hh_irq_label label); - int hh_irq_wait_for_lend(enum hh_irq_label label, enum hh_vm_names name, hh_irq_handle_fn on_lend, void *data); +int hh_irq_wait_for_lend_v2(enum hh_irq_label label, enum hh_vm_names name, + hh_irq_handle_fn_v2 on_lend, void *data); int hh_irq_accept(enum hh_irq_label label, int irq, int type); +int hh_irq_accept_notify(enum hh_irq_label label); int hh_irq_release(enum hh_irq_label label); +int hh_irq_release_notify(enum hh_irq_label label); #endif diff --git a/include/linux/haven/hh_rm_drv.h b/include/linux/haven/hh_rm_drv.h index b9beedc4f60c..710d33c2f6b9 100644 --- a/include/linux/haven/hh_rm_drv.h +++ b/include/linux/haven/hh_rm_drv.h @@ -141,6 +141,7 @@ struct hh_notify_vmid_desc { #define HH_RM_NOTIF_VM_STATUS 0x56100008 #define HH_RM_NOTIF_VM_IRQ_LENT 0x56100011 #define HH_RM_NOTIF_VM_IRQ_RELEASED 0x56100012 +#define HH_RM_NOTIF_VM_IRQ_ACCEPTED 0x56100013 #define HH_RM_VM_STATUS_NO_STATE 0 #define HH_RM_VM_STATUS_INIT 1 @@ -177,6 +178,10 @@ struct hh_rm_notif_vm_irq_released_payload { hh_virq_handle_t virq_handle; } __packed; +struct hh_rm_notif_vm_irq_accepted_payload { + hh_virq_handle_t virq_handle; +} __packed; + /* VM Services */ #define HH_RM_NOTIF_VM_CONSOLE_CHARS 0X56100080 @@ -197,10 +202,16 @@ int hh_rm_unregister_notifier(struct notifier_block *nb); int hh_rm_virq_to_irq(u32 virq, u32 type); int hh_rm_irq_to_virq(int irq, u32 *virq); +int hh_rm_vm_irq_lend(hh_vmid_t vmid, + int virq, + int label, + hh_virq_handle_t *virq_handle); +int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle); int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq); -int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, int virq, int label, - hh_virq_handle_t *virq_handle); +int hh_rm_vm_irq_accept_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle); +int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle); int hh_rm_vm_irq_release_notify(hh_vmid_t vmid, hh_virq_handle_t virq_handle); + int hh_rm_vm_irq_reclaim(hh_virq_handle_t virq_handle); /* Client APIs for VM management */ @@ -260,23 +271,44 @@ static inline int hh_rm_virq_to_irq(u32 virq) return -EINVAL; } +static inline int hh_rm_vm_irq_lend(hh_vmid_t vmid, + int virq, + int label, + hh_virq_handle_t *virq_handle) +{ + return -EINVAL; +} + static inline int hh_rm_irq_to_virq(int irq, u32 *virq) { return -EINVAL; } +static inline int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, + hh_virq_handle_t virq_handle) +{ + return -EINVAL; +} + static inline int hh_rm_vm_irq_accept(hh_virq_handle_t virq_handle, int virq) { return -EINVAL; + } -static inline int hh_rm_vm_irq_lend_notify(hh_vmid_t vmid, int virq, int label) +static inline int hh_rm_vm_irq_accept_notify(hh_vmid_t vmid, + hh_virq_handle_t virq_handle) +{ + return -EINVAL; +} + +static inline int hh_rm_vm_irq_release(hh_virq_handle_t virq_handle) { return -EINVAL; } static inline int hh_rm_vm_irq_release_notify(hh_vmid_t vmid, - hh_virq_handle_t virq_handle) + hh_virq_handle_t virq_handle) { return -EINVAL; }