
Following is the sequence during which issue is observed: 1) HAL sends a commit with VM_REQ_RELEASE property set indicating transition from primary vm to trusted vm. 2) Before the transition commit ends, there is atomic check for next commit from HAL with VM_REQ_ACQUIRE property indicating transition from trusted vm to primary vm. 3) Since the HW is currently owned by the primary vm, it performs a early return during check phase. After this, transition has occurred from primary to trusted and when the next commit is scheduled on primary, it results in crash since it is currently not the owner. This change adds necessary to check avoid commit with VM_REQ_ACQUIRE state before the transition. Change-Id: I4650305a95ef6bc495375a21a799522e67a61883 Signed-off-by: Yashwanth <quic_yvulapu@quicinc.com>
344 lines
7.7 KiB
C
344 lines
7.7 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
|
|
|
|
#include <linux/list_sort.h>
|
|
#include "linux/sde_rsc.h"
|
|
#include "dsi/dsi_display.h"
|
|
#include "dp/dp_display.h"
|
|
#include "sde_kms.h"
|
|
#include "sde_vm_common.h"
|
|
#include "sde_crtc.h"
|
|
#include "sde_vm_msgq.h"
|
|
|
|
struct gh_notify_vmid_desc *sde_vm_populate_vmid(gh_vmid_t vmid)
|
|
{
|
|
struct gh_notify_vmid_desc *vmid_desc;
|
|
|
|
vmid_desc = kzalloc(offsetof(struct gh_notify_vmid_desc,
|
|
vmid_entries[1]), GFP_KERNEL);
|
|
if (!vmid_desc)
|
|
return ERR_PTR(ENOMEM);
|
|
|
|
vmid_desc->n_vmid_entries = 1;
|
|
vmid_desc->vmid_entries[0].vmid = vmid;
|
|
|
|
return vmid_desc;
|
|
}
|
|
|
|
struct gh_acl_desc *sde_vm_populate_acl(enum gh_vm_names vm_name)
|
|
{
|
|
struct gh_acl_desc *acl_desc;
|
|
gh_vmid_t vmid;
|
|
|
|
gh_rm_get_vmid(vm_name, &vmid);
|
|
|
|
acl_desc = kzalloc(offsetof(struct gh_acl_desc, acl_entries[1]),
|
|
GFP_KERNEL);
|
|
if (!acl_desc)
|
|
return ERR_PTR(ENOMEM);
|
|
|
|
acl_desc->n_acl_entries = 1;
|
|
acl_desc->acl_entries[0].vmid = vmid;
|
|
acl_desc->acl_entries[0].perms = GH_RM_ACL_R | GH_RM_ACL_W;
|
|
|
|
return acl_desc;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0))
|
|
int __mem_sort_cmp(void *priv, const struct list_head *a, const struct list_head *b)
|
|
{
|
|
const struct msm_io_mem_entry *left =
|
|
container_of(a, struct msm_io_mem_entry, list);
|
|
const struct msm_io_mem_entry *right =
|
|
container_of(b, struct msm_io_mem_entry, list);
|
|
|
|
return (left->base - right->base);
|
|
}
|
|
#else
|
|
int __mem_sort_cmp(void *priv, struct list_head *a, struct list_head *b)
|
|
{
|
|
struct msm_io_mem_entry *left =
|
|
container_of(a, struct msm_io_mem_entry, list);
|
|
struct msm_io_mem_entry *right =
|
|
container_of(b, struct msm_io_mem_entry, list);
|
|
|
|
return (left->base - right->base);
|
|
}
|
|
#endif
|
|
|
|
bool __merge_on_overlap(struct msm_io_mem_entry *res,
|
|
const struct msm_io_mem_entry *left,
|
|
const struct msm_io_mem_entry *right)
|
|
{
|
|
phys_addr_t l_s = left->base;
|
|
phys_addr_t l_e = left->base + left->size;
|
|
phys_addr_t r_s = right->base;
|
|
phys_addr_t r_e = right->base + right->size;
|
|
|
|
memset(res, 0, sizeof(*res));
|
|
|
|
if (r_s <= l_e) {
|
|
res->base = min(l_s, r_s);
|
|
res->size = max(l_e, r_e) - res->base;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void _sde_vm_sort_and_align(struct list_head *mem)
|
|
{
|
|
struct msm_io_mem_entry *entry, *tmp, *prev = NULL;
|
|
struct msm_io_mem_entry merged_entry;
|
|
|
|
list_for_each_entry(entry, mem, list) {
|
|
entry->base = ALIGN_DOWN(entry->base, PAGE_SIZE);
|
|
entry->size = ALIGN(entry->size, PAGE_SIZE);
|
|
}
|
|
|
|
list_sort(NULL, mem, __mem_sort_cmp);
|
|
|
|
list_for_each_entry_safe(entry, tmp, mem, list) {
|
|
if (prev && __merge_on_overlap(&merged_entry, prev, entry)) {
|
|
prev->base = merged_entry.base;
|
|
prev->size = merged_entry.size;
|
|
|
|
list_del(&entry->list);
|
|
entry = prev;
|
|
}
|
|
prev = entry;
|
|
}
|
|
|
|
list_for_each_entry(entry, mem, list)
|
|
SDE_DEBUG("base: 0x%llx - size: 0x%llx\n",
|
|
entry->base, entry->size);
|
|
}
|
|
|
|
struct gh_sgl_desc *sde_vm_populate_sgl(struct msm_io_res *io_res)
|
|
{
|
|
struct gh_sgl_desc *sgl_desc;
|
|
struct msm_io_mem_entry *mem;
|
|
u32 i = 0, num_mem_entry = 0;
|
|
|
|
_sde_vm_sort_and_align(&io_res->mem);
|
|
|
|
list_for_each_entry(mem, &io_res->mem, list)
|
|
num_mem_entry++;
|
|
|
|
sgl_desc = kzalloc(offsetof(struct gh_sgl_desc,
|
|
sgl_entries[num_mem_entry]), GFP_KERNEL);
|
|
if (!sgl_desc)
|
|
return ERR_PTR(ENOMEM);
|
|
|
|
sgl_desc->n_sgl_entries = num_mem_entry;
|
|
list_for_each_entry(mem, &io_res->mem, list) {
|
|
sgl_desc->sgl_entries[i].ipa_base = mem->base;
|
|
sgl_desc->sgl_entries[i].size = mem->size;
|
|
i++;
|
|
}
|
|
|
|
msm_dss_clean_io_mem(&io_res->mem);
|
|
|
|
return sgl_desc;
|
|
}
|
|
|
|
struct sde_vm_irq_desc *sde_vm_populate_irq(struct msm_io_res *io_res)
|
|
{
|
|
struct msm_io_irq_entry *irq;
|
|
u32 i = 0, num_irq = 0;
|
|
struct sde_vm_irq_desc *irq_desc;
|
|
|
|
list_for_each_entry(irq, &io_res->irq, list)
|
|
num_irq++;
|
|
|
|
irq_desc = kzalloc(sizeof(*irq_desc), GFP_KERNEL);
|
|
if (!irq_desc)
|
|
return ERR_PTR(ENOMEM);
|
|
|
|
irq_desc->irq_entries = kcalloc(num_irq,
|
|
sizeof(struct sde_vm_irq_entry),
|
|
GFP_KERNEL);
|
|
if (!irq_desc->irq_entries) {
|
|
sde_vm_free_irq(irq_desc);
|
|
return ERR_PTR(ENOMEM);
|
|
}
|
|
|
|
list_for_each_entry(irq, &io_res->irq, list) {
|
|
struct sde_vm_irq_entry *entry = &irq_desc->irq_entries[i];
|
|
|
|
entry->irq = irq->irq_num;
|
|
entry->label = irq->label;
|
|
i++;
|
|
}
|
|
|
|
irq_desc->n_irq = num_irq;
|
|
|
|
msm_dss_clean_io_irq(&io_res->irq);
|
|
|
|
return irq_desc;
|
|
}
|
|
|
|
void sde_vm_free_irq(struct sde_vm_irq_desc *irq_desc)
|
|
{
|
|
if (irq_desc && irq_desc->irq_entries)
|
|
kfree(irq_desc->irq_entries);
|
|
|
|
kfree(irq_desc);
|
|
}
|
|
|
|
int sde_vm_get_resources(struct sde_kms *sde_kms, struct msm_io_res *io_res)
|
|
{
|
|
struct msm_drm_private *priv = sde_kms->dev->dev_private;
|
|
struct msm_vm_client_entry *entry;
|
|
int rc = 0;
|
|
|
|
rc = sde_kms_get_io_resources(sde_kms, io_res);
|
|
if (rc)
|
|
goto fail_get_res;
|
|
|
|
list_for_each_entry(entry, &priv->vm_client_list, list) {
|
|
if (!entry->ops.vm_get_io_resources)
|
|
continue;
|
|
|
|
rc = entry->ops.vm_get_io_resources(io_res, entry->data);
|
|
if (rc) {
|
|
SDE_ERROR("get_io_resources failed for device: %d\n",
|
|
entry->dev->id);
|
|
goto fail_get_res;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
|
|
fail_get_res:
|
|
msm_dss_clean_io_mem(&io_res->mem);
|
|
msm_dss_clean_io_irq(&io_res->irq);
|
|
|
|
return rc;
|
|
}
|
|
|
|
void sde_vm_free_resources(struct msm_io_res *io_res)
|
|
{
|
|
msm_dss_clean_io_mem(&io_res->mem);
|
|
msm_dss_clean_io_irq(&io_res->irq);
|
|
}
|
|
|
|
int sde_vm_post_acquire(struct sde_kms *kms)
|
|
{
|
|
struct msm_drm_private *priv = kms->dev->dev_private;
|
|
struct msm_vm_client_entry *entry;
|
|
int rc = 0;
|
|
|
|
list_for_each_entry(entry, &priv->vm_client_list, list) {
|
|
if (!entry->ops.vm_post_hw_acquire)
|
|
continue;
|
|
|
|
rc = entry->ops.vm_post_hw_acquire(entry->data);
|
|
if (rc) {
|
|
SDE_ERROR("post_acquire failed for device: %d\n",
|
|
entry->dev->id);
|
|
goto post_acquire_rollback;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
|
|
post_acquire_rollback:
|
|
list_for_each_entry_continue_reverse(entry, &priv->vm_client_list,
|
|
list) {
|
|
if (!entry->ops.vm_pre_hw_release)
|
|
continue;
|
|
|
|
rc = entry->ops.vm_pre_hw_release(entry->data);
|
|
if (rc) {
|
|
SDE_ERROR(
|
|
"post_acquire failed during rollback for device: %d\n",
|
|
entry->dev->id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int sde_vm_pre_release(struct sde_kms *kms)
|
|
{
|
|
struct msm_drm_private *priv = kms->dev->dev_private;
|
|
struct msm_vm_client_entry *entry;
|
|
int rc = 0;
|
|
|
|
list_for_each_entry(entry, &priv->vm_client_list, list) {
|
|
if (!entry->ops.vm_pre_hw_release)
|
|
continue;
|
|
|
|
rc = entry->ops.vm_pre_hw_release(entry->data);
|
|
if (rc) {
|
|
SDE_ERROR("pre_release failed for device: %d\n",
|
|
entry->dev->id);
|
|
goto pre_release_rollback;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
|
|
pre_release_rollback:
|
|
list_for_each_entry_continue_reverse(entry, &priv->vm_client_list,
|
|
list) {
|
|
if (!entry->ops.vm_post_hw_acquire)
|
|
continue;
|
|
|
|
rc = entry->ops.vm_post_hw_acquire(entry->data);
|
|
if (rc) {
|
|
SDE_ERROR(
|
|
"post_acquire failed during rollback for device: %d\n",
|
|
entry->dev->id);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int sde_vm_request_valid(struct sde_kms *sde_kms,
|
|
enum sde_crtc_vm_req old_state,
|
|
enum sde_crtc_vm_req new_state)
|
|
{
|
|
int rc = 0;
|
|
bool vm_owns_hw = sde_vm_owns_hw(sde_kms);
|
|
|
|
switch (new_state) {
|
|
case VM_REQ_RELEASE:
|
|
case VM_REQ_NONE:
|
|
if ((old_state == VM_REQ_RELEASE) || !vm_owns_hw)
|
|
rc = -EINVAL;
|
|
break;
|
|
case VM_REQ_ACQUIRE:
|
|
if ((old_state != VM_REQ_RELEASE) || vm_owns_hw)
|
|
rc = -EINVAL;
|
|
break;
|
|
default:
|
|
SDE_ERROR("invalid vm request\n");
|
|
rc = -EINVAL;
|
|
};
|
|
|
|
SDE_DEBUG("old req: %d new req: %d owns_hw: %d, rc: %d\n",
|
|
old_state, new_state, vm_owns_hw, rc);
|
|
SDE_EVT32(old_state, new_state, vm_owns_hw, rc);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int sde_vm_msg_send(struct sde_vm *sde_vm, void *msg, size_t msg_size)
|
|
{
|
|
if (!sde_vm)
|
|
return -EINVAL;
|
|
|
|
return sde_vm_msgq_send(sde_vm, msg, msg_size);
|
|
}
|