* [PATCH v3 0/6] drm/panthor: Protected mode support for Mali CSF GPUs
@ 2026-09-11 11:40 Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 1/6] drm/panthor: De-duplicate FW memory section sync Ketil Johnsen
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Ketil Johnsen @ 2026-09-11 11:40 UTC (permalink / raw)
To: Boris Brezillon, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Daniel Almeida, Alice Ryhl
Cc: dri-devel, linux-kernel, Ketil Johnsen
Hi,
This is a patch series covering the support for protected mode execution in
Mali Panthor CSF kernel driver.
It builds on the initial RFC posted by Florent Tomasin back in January of 2025.
The initial RFC can be found here:
https://lore.kernel.org/lkml/cover.1738228114.git.florent.tomasin@arm.com/
The Mali CSF GPUs come with the support for protected mode execution at the
HW level. This feature requires two main changes in the kernel driver:
1) Configure the GPU with a protected buffer. The system must provide a DMA
heap from which the driver can get protected buffers.
It can be a carved-out memory or dynamically allocated protected memory region.
Some system includes a trusted FW which is in charge of the protected memory.
This problem is integration specific and the responsibility to allocate the
required protected memory from the correct and system specific DMA heap is
left to user space (Mesa). Panthor CSF driver must be handed the needed
buffers via new IOCTLs.
2) Handle enter and exit of the GPU HW from normal to protected mode of execution.
FW sends a request for protected mode entry to the kernel driver.
The acknowledgment of that request is a scheduling decision. Effectively,
protected mode execution should not overrule normal mode of execution.
A fair distribution of execution time will guaranty the overall performance
of the device, including the UI (usually executing in normal mode),
will not regress when a protected mode job is submitted by an application.
Background
----------
Current Mali Panthor CSF driver does not allow a user space application to
execute protected jobs on the GPU. This use case is quite common on end-user-device.
A user may want to watch a video or render content that is under a "Digital Right
Management" protection, or launch an application with user private data.
1) User-space:
In order for an application to execute protected jobs on a Mali CSF GPU the
user space application must submit jobs to the GPU within a "protected regions"
(range of commands to execute in protected mode).
Find here an example of a command buffer that contains protected commands:
```
<--- Normal mode ---><--- Protected mode ---><--- Normal mode --->
+-------------------------------------------------------------------------+
| ... | CMD_0 | ... | CMD_N | PROT_REGION | CMD_N+1 | ... | CMD_N+M | ... |
+-------------------------------------------------------------------------+
```
The PROT_REGION command acts as a barrier to notify the HW of upcoming
protected jobs. It also defines the number of commands to execute in protected
mode.
The Mesa definition of the opcode can be found here:
https://gitlab.freedesktop.org/mesa/mesa/-/blob/main/src/panfrost/lib/genxml/v10.xml?ref_type=heads#L763
2) Kernel-space:
When loading the FW image, the Kernel driver must also load the data section
of CSF FW that comes from the protected memory, in order to allow FW to
execute in protected mode.
The FW uses the same protected memory buffer to execute protected jobs from
all processes on the system. Note: This buffer must be provided by a user
space process with CAP_SYS_MODULE though.
In addition, when a CSG (group) is created, it must have a protected suspend
buffer. User space must provide this buffer on group creation if it want to
execute protected jobs.
The required size of both these buffer types can be queried. See
query type DRM_PANTHOR_DEV_QUERY_PROTM_INFO.
Design decisions
----------------
The earlier versions (RFC and v1) relied on in-kernel allocation of protected
DMA-bufs, both for the required FW memory (system wide), and per group
suspend buffers (per process). This is no longer needed.
Starting with v2, this is now changed and it is user space which is responsible
for supplying these buffers.
Only a user space processes with elevated privileges (CAP_SYS_MODULE) is trusted
to provide the system wide protected FW memory. This only needs to happen once
per boot, and is needed before any non-privileged processes can make use of
protected rendering.
The Mali Panthor CSF kernel driver will handle enter/exit of protected
mode with a fair consideration of the job scheduling.
If the system integrator does not provide a protected DMA heap, the driver
will not allow any protected mode execution.
Patch series
------------
[PATCHES 1-4]:
These are refactoring to aid the implementation of the protected rendering
feature itself.
* drm/panthor: De-duplicate FW memory section sync
* drm/panthor: Minor scheduler refactoring
* drm/panthor: Pass drm_file instead of panthor_file
* drm/panthor: Don't allocate protm_suspend_buf
[PATCH 5]:
This patch implements the logic to handle enter/exit of the GPU protected
mode in Panthor CSF driver.
Note: To ease the handling of faults from protected mode, only a single CSG is
allowed to execute in protected mode. It must be the top priority one.
* drm/panthor: Add support for entering and exiting protected mode
[PATCH 7]:
The final patch exposes this feature via the uAPI and adds the necessary
handling/use of user space provided protected memory.
* drm/panthor: Expose protected rendering features
Testing
-------
1) Platform and development environment
Any platform containing a Mali CSF type of GPU and a protected memory allocator
that is based on DMA Heap can be used. For example, it can be a physical platform
or a simulator such as Arm Total Compute FVPs platforms. Reference to the latter:
https://developer.arm.com/Tools%20and%20Software/Fixed%20Virtual%20Platforms/Total%20Compute%20FVPs
2) Mesa:
PanVK support can be found here:
https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40044
Previous versions
-----------------
v2: https://lkml.org/lkml/2026/7/12/369
v1: https://lkml.org/lkml/2026/5/5/1523
Highlevel changes
-----------------
Changes in v3:
- Dropped patch "drm/panthor: Explicit expansion of locked VM region"
- See each patch for detailed changes
Changes in v2:
- Conceptually similar to v1, but heavily reworked to address fault handling
- User space provides all protected memory buffers (no in-kernel dmabuf alloc)
Boris Brezillon (2):
drm/panthor: Pass drm_file instead of panthor_file
drm/panthor: Expose protected rendering features
Florent Tomasin (2):
drm/panthor: Minor scheduler refactoring
drm/panthor: Add support for entering and exiting protected mode
Ketil Johnsen (2):
drm/panthor: De-duplicate FW memory section sync
drm/panthor: Don't allocate protm_suspend_buf
drivers/gpu/drm/panthor/panthor_device.c | 1 +
drivers/gpu/drm/panthor/panthor_device.h | 34 ++
drivers/gpu/drm/panthor/panthor_drv.c | 61 ++-
drivers/gpu/drm/panthor/panthor_fw.c | 274 +++++++++--
drivers/gpu/drm/panthor/panthor_fw.h | 6 +
drivers/gpu/drm/panthor/panthor_gem.c | 106 +++--
drivers/gpu/drm/panthor/panthor_gem.h | 7 +-
drivers/gpu/drm/panthor/panthor_gpu.c | 47 +-
drivers/gpu/drm/panthor/panthor_gpu.h | 4 +
drivers/gpu/drm/panthor/panthor_mmu.c | 35 +-
drivers/gpu/drm/panthor/panthor_mmu.h | 6 +-
drivers/gpu/drm/panthor/panthor_sched.c | 567 +++++++++++++++++++----
drivers/gpu/drm/panthor/panthor_sched.h | 27 +-
include/uapi/drm/panthor_drm.h | 85 +++-
14 files changed, 1033 insertions(+), 227 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v3 1/6] drm/panthor: De-duplicate FW memory section sync
2026-09-11 11:40 [PATCH v3 0/6] drm/panthor: Protected mode support for Mali CSF GPUs Ketil Johnsen
@ 2026-09-11 11:40 ` Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 2/6] drm/panthor: Minor scheduler refactoring Ketil Johnsen
` (4 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Ketil Johnsen @ 2026-09-11 11:40 UTC (permalink / raw)
To: Boris Brezillon, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Daniel Almeida, Alice Ryhl
Cc: dri-devel, linux-kernel, Ketil Johnsen
Handle the sync to device of FW memory sections inside
panthor_fw_init_section_mem() so that the callers do not have to.
This small improvement is also critical for protected FW sections,
so we avoid issuing memory transactions to protected memory from
CPU running in normal mode.
Signed-off-by: Ketil Johnsen <ketil.johnsen@arm.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Reviewed-by: Steven Price <steven.price@arm.com>
---
drivers/gpu/drm/panthor/panthor_fw.c | 23 +++++++----------------
1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 4f1fab66a13bf..5f9f7a92c56a8 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -449,6 +449,7 @@ static void panthor_fw_init_section_mem(struct panthor_device *ptdev,
struct panthor_fw_section *section)
{
bool was_mapped = !!section->mem->kmap;
+ struct sg_table *sgt;
int ret;
if (!section->data.size &&
@@ -467,6 +468,12 @@ static void panthor_fw_init_section_mem(struct panthor_device *ptdev,
if (!was_mapped)
panthor_kernel_bo_vunmap(section->mem);
+
+ /* An sgt should have been requested when the kernel BO was GPU-mapped. */
+ sgt = to_panthor_bo(section->mem->obj)->dmap.sgt;
+ if (!drm_WARN_ON_ONCE(&ptdev->base, !sgt))
+ dma_sync_sgtable_for_device(ptdev->base.dev, sgt,
+ DMA_TO_DEVICE);
}
/**
@@ -639,7 +646,6 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
if (section_size) {
u32 cache_mode = hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_MASK;
- struct panthor_gem_object *bo;
u32 vm_map_flags = 0;
u64 va = hdr.va.start;
@@ -676,14 +682,6 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
}
panthor_fw_init_section_mem(ptdev, section);
-
- bo = to_panthor_bo(section->mem->obj);
-
- /* An sgt should have been requested when the kernel BO was GPU-mapped. */
- if (drm_WARN_ON_ONCE(&ptdev->base, !bo->dmap.sgt))
- return -EINVAL;
-
- dma_sync_sgtable_for_device(ptdev->base.dev, bo->dmap.sgt, DMA_TO_DEVICE);
}
if (hdr.va.start == CSF_MCU_SHARED_REGION_START)
@@ -738,17 +736,10 @@ panthor_reload_fw_sections(struct panthor_device *ptdev, bool full_reload)
struct panthor_fw_section *section;
list_for_each_entry(section, &ptdev->fw->sections, node) {
- struct sg_table *sgt;
-
if (!full_reload && !(section->flags & CSF_FW_BINARY_IFACE_ENTRY_WR))
continue;
panthor_fw_init_section_mem(ptdev, section);
-
- /* An sgt should have been requested when the kernel BO was GPU-mapped. */
- sgt = to_panthor_bo(section->mem->obj)->dmap.sgt;
- if (!drm_WARN_ON_ONCE(&ptdev->base, !sgt))
- dma_sync_sgtable_for_device(ptdev->base.dev, sgt, DMA_TO_DEVICE);
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 2/6] drm/panthor: Minor scheduler refactoring
2026-09-11 11:40 [PATCH v3 0/6] drm/panthor: Protected mode support for Mali CSF GPUs Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 1/6] drm/panthor: De-duplicate FW memory section sync Ketil Johnsen
@ 2026-09-11 11:40 ` Ketil Johnsen
2026-09-11 14:23 ` Boris Brezillon
2026-09-11 11:40 ` [PATCH v3 3/6] drm/panthor: Pass drm_file instead of panthor_file Ketil Johnsen
` (3 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Ketil Johnsen @ 2026-09-11 11:40 UTC (permalink / raw)
To: Boris Brezillon, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Daniel Almeida, Alice Ryhl
Cc: dri-devel, linux-kernel, Florent Tomasin, Ketil Johnsen
From: Florent Tomasin <florent.tomasin@arm.com>
Refactor parts of the group scheduling logic into new helper functions.
This will simplify addition of the protected mode feature.
Remove redundant assignments of csg_slot.
Signed-off-by: Florent Tomasin <florent.tomasin@arm.com>
Co-developed-by: Ketil Johnsen <ketil.johnsen@arm.com>
Signed-off-by: Ketil Johnsen <ketil.johnsen@arm.com>
---
v3:
- Use panthor_fw_csg_endpoint_req_update() (was rebase mistake)
- Function rename to tick_ctx_update_group_prio()
v2:
- Moved option to ding only the CSG doorbell to later patch
---
drivers/gpu/drm/panthor/panthor_sched.c | 131 ++++++++++++++----------
1 file changed, 79 insertions(+), 52 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 60b2417deb81b..1123cf36a7bca 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -2344,12 +2344,81 @@ tick_ctx_cleanup(struct panthor_scheduler *sched,
}
}
+static void
+tick_ctx_evict_group(struct panthor_scheduler *sched,
+ struct panthor_csg_slots_upd_ctx *upd_ctx,
+ struct panthor_group *group)
+{
+ struct panthor_device *ptdev = sched->ptdev;
+
+ if (drm_WARN_ON(&ptdev->base, group->csg_id < 0))
+ return;
+
+ csgs_upd_ctx_queue_reqs(ptdev, upd_ctx, group->csg_id,
+ group_can_run(group) ?
+ CSG_STATE_SUSPEND : CSG_STATE_TERMINATE,
+ CSG_STATE_MASK);
+}
+
+static void
+tick_ctx_update_group_prio(struct panthor_scheduler *sched,
+ struct panthor_csg_slots_upd_ctx *upd_ctx,
+ struct panthor_group *group,
+ int new_csg_prio)
+{
+ struct panthor_device *ptdev = sched->ptdev;
+ struct panthor_fw_csg_iface *csg_iface;
+ struct panthor_csg_slot *csg_slot;
+
+ if (group->csg_id < 0)
+ return;
+
+ csg_iface = panthor_fw_get_csg_iface(ptdev, group->csg_id);
+ csg_slot = &sched->csg_slots[group->csg_id];
+
+ if (csg_slot->priority != new_csg_prio) {
+ panthor_fw_csg_endpoint_req_update(ptdev, csg_iface,
+ CSG_EP_REQ_PRIORITY(new_csg_prio),
+ CSG_EP_REQ_PRIORITY_MASK);
+ csgs_upd_ctx_queue_reqs(ptdev, upd_ctx, group->csg_id,
+ csg_iface->output->ack ^ CSG_ENDPOINT_CONFIG,
+ CSG_ENDPOINT_CONFIG);
+ }
+}
+
+static int
+tick_ctx_schedule_group(struct panthor_scheduler *sched,
+ struct panthor_csg_slots_upd_ctx *upd_ctx,
+ struct panthor_group *group,
+ int csg_id, int csg_prio)
+{
+ struct panthor_device *ptdev = sched->ptdev;
+ struct panthor_fw_csg_iface *csg_iface =
+ panthor_fw_get_csg_iface(ptdev, csg_id);
+ int ret;
+
+ ret = group_bind_locked(group, csg_id);
+ if (ret)
+ return ret;
+
+ csg_slot_prog_locked(ptdev, csg_id, csg_prio);
+
+ csgs_upd_ctx_queue_reqs(ptdev, upd_ctx, csg_id,
+ group->state == PANTHOR_CS_GROUP_SUSPENDED ?
+ CSG_STATE_RESUME : CSG_STATE_START,
+ CSG_STATE_MASK);
+ csgs_upd_ctx_queue_reqs(ptdev, upd_ctx, csg_id,
+ csg_iface->output->ack ^ CSG_ENDPOINT_CONFIG,
+ CSG_ENDPOINT_CONFIG);
+
+ return 0;
+}
+
static void
tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *ctx)
{
struct panthor_group *group, *tmp;
struct panthor_device *ptdev = sched->ptdev;
- struct panthor_csg_slot *csg_slot;
int prio, new_csg_prio = MAX_CSG_PRIO, i;
u32 free_csg_slots = 0;
struct panthor_csg_slots_upd_ctx upd_ctx;
@@ -2359,44 +2428,13 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c
for (prio = PANTHOR_CSG_PRIORITY_COUNT - 1; prio >= 0; prio--) {
/* Suspend or terminate evicted groups. */
- list_for_each_entry(group, &ctx->old_groups[prio], run_node) {
- bool term = !group_can_run(group);
- int csg_id = group->csg_id;
-
- if (drm_WARN_ON(&ptdev->base, csg_id < 0))
- continue;
-
- csg_slot = &sched->csg_slots[csg_id];
- csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id,
- term ? CSG_STATE_TERMINATE : CSG_STATE_SUSPEND,
- CSG_STATE_MASK);
- }
+ list_for_each_entry(group, &ctx->old_groups[prio], run_node)
+ tick_ctx_evict_group(sched, &upd_ctx, group);
/* Update priorities on already running groups. */
- list_for_each_entry(group, &ctx->groups[prio], run_node) {
- struct panthor_fw_csg_iface *csg_iface;
- int csg_id = group->csg_id;
-
- if (csg_id < 0) {
- new_csg_prio--;
- continue;
- }
-
- csg_slot = &sched->csg_slots[csg_id];
- csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id);
- if (csg_slot->priority == new_csg_prio) {
- new_csg_prio--;
- continue;
- }
-
- panthor_fw_csg_endpoint_req_update(ptdev, csg_iface,
- CSG_EP_REQ_PRIORITY(new_csg_prio),
- CSG_EP_REQ_PRIORITY_MASK);
- csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id,
- csg_iface->output->ack ^ CSG_ENDPOINT_CONFIG,
- CSG_ENDPOINT_CONFIG);
- new_csg_prio--;
- }
+ list_for_each_entry(group, &ctx->groups[prio], run_node)
+ tick_ctx_update_group_prio(sched, &upd_ctx, group,
+ new_csg_prio--);
}
ret = csgs_upd_ctx_apply_locked(ptdev, &upd_ctx);
@@ -2424,34 +2462,23 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c
for (prio = PANTHOR_CSG_PRIORITY_COUNT - 1; prio >= 0; prio--) {
list_for_each_entry(group, &ctx->groups[prio], run_node) {
int csg_id = group->csg_id;
- struct panthor_fw_csg_iface *csg_iface;
+ int csg_prio = new_csg_prio--;
- if (csg_id >= 0) {
- new_csg_prio--;
+ if (csg_id >= 0)
continue;
- }
csg_id = ffs(free_csg_slots) - 1;
if (drm_WARN_ON(&ptdev->base, csg_id < 0))
break;
- csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id);
- csg_slot = &sched->csg_slots[csg_id];
- ret = group_bind_locked(group, csg_id);
+ ret = tick_ctx_schedule_group(sched, &upd_ctx, group,
+ csg_id, csg_prio);
if (ret) {
panthor_device_schedule_reset(ptdev);
ctx->csg_upd_failed_mask |= BIT(csg_id);
return;
}
- csg_slot_prog_locked(ptdev, csg_id, new_csg_prio--);
- csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id,
- group->state == PANTHOR_CS_GROUP_SUSPENDED ?
- CSG_STATE_RESUME : CSG_STATE_START,
- CSG_STATE_MASK);
- csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id,
- csg_iface->output->ack ^ CSG_ENDPOINT_CONFIG,
- CSG_ENDPOINT_CONFIG);
free_csg_slots &= ~BIT(csg_id);
}
}
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 3/6] drm/panthor: Pass drm_file instead of panthor_file
2026-09-11 11:40 [PATCH v3 0/6] drm/panthor: Protected mode support for Mali CSF GPUs Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 1/6] drm/panthor: De-duplicate FW memory section sync Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 2/6] drm/panthor: Minor scheduler refactoring Ketil Johnsen
@ 2026-09-11 11:40 ` Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 4/6] drm/panthor: Don't allocate protm_suspend_buf Ketil Johnsen
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Ketil Johnsen @ 2026-09-11 11:40 UTC (permalink / raw)
To: Boris Brezillon, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Daniel Almeida, Alice Ryhl
Cc: dri-devel, linux-kernel, Ketil Johnsen
From: Boris Brezillon <boris.brezillon@collabora.com>
Some sched helpers need info that are part of drm_file, and we will
soon need to call drm_gem_object_lookup() from panthor_group_create().
Let's prepare for that by passing a drm_file instead of panthor_file to
all current helpers taking a panthor_file, so we keep things consistent.
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Co-developed-by: Ketil Johnsen <ketil.johnsen@arm.com>
Signed-off-by: Ketil Johnsen <ketil.johnsen@arm.com>
---
v3:
- Fixed incorrect parameter passed to panthor_gpu_show_fdinfo()
v2:
- First introduction of this patch in this patch set
---
drivers/gpu/drm/panthor/panthor_drv.c | 38 +++++++++++--------------
drivers/gpu/drm/panthor/panthor_mmu.c | 11 ++++---
drivers/gpu/drm/panthor/panthor_mmu.h | 6 ++--
drivers/gpu/drm/panthor/panthor_sched.c | 37 ++++++++++++++----------
drivers/gpu/drm/panthor/panthor_sched.h | 23 +++++++--------
5 files changed, 59 insertions(+), 56 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index 46a3080b0b206..51eb77633602f 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -1111,7 +1111,6 @@ static int panthor_ioctl_bo_mmap_offset(struct drm_device *ddev, void *data,
static int panthor_ioctl_group_submit(struct drm_device *ddev, void *data,
struct drm_file *file)
{
- struct panthor_file *pfile = file->driver_priv;
struct drm_panthor_group_submit *args = data;
struct drm_panthor_queue_submit *jobs_args;
struct panthor_submit_ctx ctx;
@@ -1136,8 +1135,7 @@ static int panthor_ioctl_group_submit(struct drm_device *ddev, void *data,
const struct drm_panthor_queue_submit *qsubmit = &jobs_args[i];
struct drm_sched_job *job;
- job = panthor_job_create(pfile, args->group_handle, qsubmit,
- file->client_id);
+ job = panthor_job_create(file, args->group_handle, qsubmit);
if (IS_ERR(job)) {
ret = PTR_ERR(job);
goto out_cleanup_submit_ctx;
@@ -1217,19 +1215,17 @@ static int panthor_ioctl_group_submit(struct drm_device *ddev, void *data,
static int panthor_ioctl_group_destroy(struct drm_device *ddev, void *data,
struct drm_file *file)
{
- struct panthor_file *pfile = file->driver_priv;
struct drm_panthor_group_destroy *args = data;
if (args->pad)
return -EINVAL;
- return panthor_group_destroy(pfile, args->group_handle);
+ return panthor_group_destroy(file, args->group_handle);
}
static int panthor_ioctl_group_create(struct drm_device *ddev, void *data,
struct drm_file *file)
{
- struct panthor_file *pfile = file->driver_priv;
struct drm_panthor_group_create *args = data;
struct drm_panthor_queue_create *queue_args;
int ret;
@@ -1245,7 +1241,7 @@ static int panthor_ioctl_group_create(struct drm_device *ddev, void *data,
if (ret)
goto out;
- ret = panthor_group_create(pfile, args, queue_args, file->client_id);
+ ret = panthor_group_create(file, args, queue_args);
if (ret < 0)
goto out;
args->group_handle = ret;
@@ -1259,10 +1255,9 @@ static int panthor_ioctl_group_create(struct drm_device *ddev, void *data,
static int panthor_ioctl_group_get_state(struct drm_device *ddev, void *data,
struct drm_file *file)
{
- struct panthor_file *pfile = file->driver_priv;
struct drm_panthor_group_get_state *args = data;
- return panthor_group_get_state(pfile, args);
+ return panthor_group_get_state(file, args);
}
static int panthor_ioctl_tiler_heap_create(struct drm_device *ddev, void *data,
@@ -1605,6 +1600,7 @@ panthor_open(struct drm_device *ddev, struct drm_file *file)
if (!pfile)
return -ENOMEM;
+ file->driver_priv = pfile;
pfile->ptdev = ptdev;
pfile->user_mmio.offset = DRM_PANTHOR_USER_MMIO_OFFSET;
@@ -1619,19 +1615,18 @@ panthor_open(struct drm_device *ddev, struct drm_file *file)
#endif
- ret = panthor_vm_pool_create(pfile);
+ ret = panthor_vm_pool_create(file);
if (ret)
goto err_free_file;
- ret = panthor_group_pool_create(pfile);
+ ret = panthor_group_pool_create(file);
if (ret)
goto err_destroy_vm_pool;
- file->driver_priv = pfile;
return 0;
err_destroy_vm_pool:
- panthor_vm_pool_destroy(pfile);
+ panthor_vm_pool_destroy(file);
err_free_file:
kfree(pfile);
@@ -1643,8 +1638,8 @@ panthor_postclose(struct drm_device *ddev, struct drm_file *file)
{
struct panthor_file *pfile = file->driver_priv;
- panthor_group_pool_destroy(pfile);
- panthor_vm_pool_destroy(pfile);
+ panthor_group_pool_destroy(file);
+ panthor_vm_pool_destroy(file);
kfree(pfile);
}
@@ -1704,11 +1699,13 @@ static int panthor_mmap(struct file *filp, struct vm_area_struct *vma)
}
static void panthor_gpu_show_fdinfo(struct panthor_device *ptdev,
- struct panthor_file *pfile,
+ struct drm_file *file,
struct drm_printer *p)
{
+ struct panthor_file *pfile = file->driver_priv;
+
if (ptdev->profile_mask & PANTHOR_DEVICE_PROFILING_ALL)
- panthor_fdinfo_gather_group_samples(pfile);
+ panthor_fdinfo_gather_group_samples(file);
if (ptdev->profile_mask & PANTHOR_DEVICE_PROFILING_TIMESTAMP) {
#ifdef CONFIG_ARM_ARCH_TIMER
@@ -1728,11 +1725,10 @@ static void panthor_gpu_show_fdinfo(struct panthor_device *ptdev,
static void panthor_show_internal_memory_stats(struct drm_printer *p, struct drm_file *file)
{
char *drv_name = file->minor->dev->driver->name;
- struct panthor_file *pfile = file->driver_priv;
struct drm_memory_stats stats = {0};
- panthor_fdinfo_gather_group_mem_info(pfile, &stats);
- panthor_vm_heaps_sizes(pfile, &stats);
+ panthor_fdinfo_gather_group_mem_info(file, &stats);
+ panthor_vm_heaps_sizes(file, &stats);
drm_fdinfo_print_size(p, drv_name, "resident", "memory", stats.resident);
drm_fdinfo_print_size(p, drv_name, "active", "memory", stats.active);
@@ -1743,7 +1739,7 @@ static void panthor_show_fdinfo(struct drm_printer *p, struct drm_file *file)
struct drm_device *dev = file->minor->dev;
struct panthor_device *ptdev = container_of(dev, struct panthor_device, base);
- panthor_gpu_show_fdinfo(ptdev, file->driver_priv, p);
+ panthor_gpu_show_fdinfo(ptdev, file, p);
panthor_show_internal_memory_stats(p, file);
drm_show_memory_stats(p, file);
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index d75d575473da4..3e5f20768d545 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -1692,8 +1692,9 @@ panthor_vm_pool_get_vm(struct panthor_vm_pool *pool, u32 handle)
* Note that VMs can outlive the pool they were created from if other
* objects hold a reference to there VMs.
*/
-void panthor_vm_pool_destroy(struct panthor_file *pfile)
+void panthor_vm_pool_destroy(struct drm_file *file)
{
+ struct panthor_file *pfile = file->driver_priv;
struct panthor_vm *vm;
unsigned long i;
@@ -1715,8 +1716,9 @@ void panthor_vm_pool_destroy(struct panthor_file *pfile)
*
* Return: 0 on success, a negative error code otherwise.
*/
-int panthor_vm_pool_create(struct panthor_file *pfile)
+int panthor_vm_pool_create(struct drm_file *file)
{
+ struct panthor_file *pfile = file->driver_priv;
struct panthor_gem_object *dummy;
int ret;
@@ -1737,7 +1739,7 @@ int panthor_vm_pool_create(struct panthor_file *pfile)
return 0;
err_destroy_vm_pool:
- panthor_vm_pool_destroy(pfile);
+ panthor_vm_pool_destroy(file);
return ret;
}
@@ -2157,8 +2159,9 @@ struct panthor_heap_pool *panthor_vm_get_heap_pool(struct panthor_vm *vm, bool c
* Calculate all heap chunk sizes in all heap pools bound to a VM. If the VM
* is active, record the size as active as well.
*/
-void panthor_vm_heaps_sizes(struct panthor_file *pfile, struct drm_memory_stats *stats)
+void panthor_vm_heaps_sizes(struct drm_file *file, struct drm_memory_stats *stats)
{
+ struct panthor_file *pfile = file->driver_priv;
struct panthor_vm *vm;
unsigned long i;
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.h b/drivers/gpu/drm/panthor/panthor_mmu.h
index abc36e7204be0..e262fe2303f6c 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.h
+++ b/drivers/gpu/drm/panthor/panthor_mmu.h
@@ -38,7 +38,7 @@ int panthor_vm_as(struct panthor_vm *vm);
struct panthor_heap_pool *
panthor_vm_get_heap_pool(struct panthor_vm *vm, bool create);
-void panthor_vm_heaps_sizes(struct panthor_file *pfile, struct drm_memory_stats *stats);
+void panthor_vm_heaps_sizes(struct drm_file *file, struct drm_memory_stats *stats);
struct panthor_vm *panthor_vm_get(struct panthor_vm *vm);
void panthor_vm_put(struct panthor_vm *vm);
@@ -64,8 +64,8 @@ void panthor_vm_add_job_fence_to_bos_resvs(struct panthor_vm *vm,
struct dma_resv *panthor_vm_resv(struct panthor_vm *vm);
struct drm_gem_object *panthor_vm_root_gem(struct panthor_vm *vm);
-void panthor_vm_pool_destroy(struct panthor_file *pfile);
-int panthor_vm_pool_create(struct panthor_file *pfile);
+void panthor_vm_pool_destroy(struct drm_file *file);
+int panthor_vm_pool_create(struct drm_file *file);
int panthor_vm_pool_create_vm(struct panthor_device *ptdev,
struct panthor_vm_pool *pool,
struct drm_panthor_vm_create *args);
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 1123cf36a7bca..e02062e8443ec 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -3112,8 +3112,9 @@ void panthor_sched_post_reset(struct panthor_device *ptdev, bool reset_failed)
}
}
-void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile)
+void panthor_fdinfo_gather_group_samples(struct drm_file *file)
{
+ struct panthor_file *pfile = file->driver_priv;
struct panthor_group_pool *gpool = pfile->groups;
struct panthor_group *group;
unsigned long i;
@@ -3649,11 +3650,11 @@ static void add_group_kbo_sizes(struct panthor_device *ptdev,
#define MAX_GROUPS_PER_POOL 128
-int panthor_group_create(struct panthor_file *pfile,
+int panthor_group_create(struct drm_file *file,
const struct drm_panthor_group_create *group_args,
- const struct drm_panthor_queue_create *queue_args,
- u64 drm_client_id)
+ const struct drm_panthor_queue_create *queue_args)
{
+ struct panthor_file *pfile = file->driver_priv;
struct panthor_device *ptdev = pfile->ptdev;
struct panthor_group_pool *gpool = pfile->groups;
struct panthor_scheduler *sched = ptdev->scheduler;
@@ -3748,7 +3749,8 @@ int panthor_group_create(struct panthor_file *pfile,
goto err_put_group;
for (i = 0; i < group_args->queues.count; i++) {
- group->queues[i] = group_create_queue(group, &queue_args[i], drm_client_id, gid, i);
+ group->queues[i] = group_create_queue(group, &queue_args[i],
+ file->client_id, gid, i);
if (IS_ERR(group->queues[i])) {
ret = PTR_ERR(group->queues[i]);
group->queues[i] = NULL;
@@ -3788,8 +3790,9 @@ int panthor_group_create(struct panthor_file *pfile,
return ret;
}
-int panthor_group_destroy(struct panthor_file *pfile, u32 group_handle)
+int panthor_group_destroy(struct drm_file *file, u32 group_handle)
{
+ struct panthor_file *pfile = file->driver_priv;
struct panthor_group_pool *gpool = pfile->groups;
struct panthor_device *ptdev = pfile->ptdev;
struct panthor_scheduler *sched = ptdev->scheduler;
@@ -3834,9 +3837,10 @@ static struct panthor_group *group_from_handle(struct panthor_group_pool *pool,
return group;
}
-int panthor_group_get_state(struct panthor_file *pfile,
+int panthor_group_get_state(struct drm_file *file,
struct drm_panthor_group_get_state *get_state)
{
+ struct panthor_file *pfile = file->driver_priv;
struct panthor_group_pool *gpool = pfile->groups;
struct panthor_device *ptdev = pfile->ptdev;
struct panthor_scheduler *sched = ptdev->scheduler;
@@ -3867,8 +3871,9 @@ int panthor_group_get_state(struct panthor_file *pfile,
return 0;
}
-int panthor_group_pool_create(struct panthor_file *pfile)
+int panthor_group_pool_create(struct drm_file *file)
{
+ struct panthor_file *pfile = file->driver_priv;
struct panthor_group_pool *gpool;
gpool = kzalloc_obj(*gpool);
@@ -3880,8 +3885,9 @@ int panthor_group_pool_create(struct panthor_file *pfile)
return 0;
}
-void panthor_group_pool_destroy(struct panthor_file *pfile)
+void panthor_group_pool_destroy(struct drm_file *file)
{
+ struct panthor_file *pfile = file->driver_priv;
struct panthor_group_pool *gpool = pfile->groups;
struct panthor_group *group;
unsigned long i;
@@ -3890,7 +3896,7 @@ void panthor_group_pool_destroy(struct panthor_file *pfile)
return;
xa_for_each(&gpool->xa, i, group)
- panthor_group_destroy(pfile, i);
+ panthor_group_destroy(file, i);
xa_destroy(&gpool->xa);
kfree(gpool);
@@ -3905,9 +3911,10 @@ void panthor_group_pool_destroy(struct panthor_file *pfile)
*
*/
void
-panthor_fdinfo_gather_group_mem_info(struct panthor_file *pfile,
+panthor_fdinfo_gather_group_mem_info(struct drm_file *file,
struct drm_memory_stats *stats)
{
+ struct panthor_file *pfile = file->driver_priv;
struct panthor_group_pool *gpool = pfile->groups;
struct panthor_group *group;
unsigned long i;
@@ -3970,11 +3977,11 @@ struct panthor_vm *panthor_job_vm(struct drm_sched_job *sched_job)
}
struct drm_sched_job *
-panthor_job_create(struct panthor_file *pfile,
+panthor_job_create(struct drm_file *file,
u16 group_handle,
- const struct drm_panthor_queue_submit *qsubmit,
- u64 drm_client_id)
+ const struct drm_panthor_queue_submit *qsubmit)
{
+ struct panthor_file *pfile = file->driver_priv;
struct panthor_group_pool *gpool = pfile->groups;
struct panthor_job *job;
u32 credits;
@@ -4045,7 +4052,7 @@ panthor_job_create(struct panthor_file *pfile,
ret = drm_sched_job_init(&job->base,
&job->group->queues[job->queue_idx]->entity,
- credits, job->group, drm_client_id);
+ credits, job->group, file->client_id);
if (ret)
goto err_put_job;
diff --git a/drivers/gpu/drm/panthor/panthor_sched.h b/drivers/gpu/drm/panthor/panthor_sched.h
index 9a8692de8aded..be7e1c8b4f563 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.h
+++ b/drivers/gpu/drm/panthor/panthor_sched.h
@@ -15,31 +15,28 @@ struct drm_panthor_queue_create;
struct drm_panthor_group_get_state;
struct drm_panthor_queue_submit;
struct panthor_device;
-struct panthor_file;
struct panthor_group_pool;
struct panthor_job;
-int panthor_group_create(struct panthor_file *pfile,
+int panthor_group_create(struct drm_file *file,
const struct drm_panthor_group_create *group_args,
- const struct drm_panthor_queue_create *queue_args,
- u64 drm_client_id);
-int panthor_group_destroy(struct panthor_file *pfile, u32 group_handle);
-int panthor_group_get_state(struct panthor_file *pfile,
+ const struct drm_panthor_queue_create *queue_args);
+int panthor_group_destroy(struct drm_file *file, u32 group_handle);
+int panthor_group_get_state(struct drm_file *file,
struct drm_panthor_group_get_state *get_state);
struct drm_sched_job *
-panthor_job_create(struct panthor_file *pfile,
+panthor_job_create(struct drm_file *file,
u16 group_handle,
- const struct drm_panthor_queue_submit *qsubmit,
- u64 drm_client_id);
+ const struct drm_panthor_queue_submit *qsubmit);
struct drm_sched_job *panthor_job_get(struct drm_sched_job *job);
struct panthor_vm *panthor_job_vm(struct drm_sched_job *sched_job);
void panthor_job_put(struct drm_sched_job *job);
void panthor_job_update_resvs(struct drm_exec *exec, struct drm_sched_job *job);
-int panthor_group_pool_create(struct panthor_file *pfile);
-void panthor_group_pool_destroy(struct panthor_file *pfile);
-void panthor_fdinfo_gather_group_mem_info(struct panthor_file *pfile,
+int panthor_group_pool_create(struct drm_file *file);
+void panthor_group_pool_destroy(struct drm_file *file);
+void panthor_fdinfo_gather_group_mem_info(struct drm_file *pfile,
struct drm_memory_stats *stats);
int panthor_sched_init(struct panthor_device *ptdev);
@@ -53,6 +50,6 @@ void panthor_sched_report_mmu_fault(struct panthor_device *ptdev);
void panthor_sched_prepare_for_vm_destruction(struct panthor_device *ptdev);
void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events);
-void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile);
+void panthor_fdinfo_gather_group_samples(struct drm_file *file);
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 4/6] drm/panthor: Don't allocate protm_suspend_buf
2026-09-11 11:40 [PATCH v3 0/6] drm/panthor: Protected mode support for Mali CSF GPUs Ketil Johnsen
` (2 preceding siblings ...)
2026-09-11 11:40 ` [PATCH v3 3/6] drm/panthor: Pass drm_file instead of panthor_file Ketil Johnsen
@ 2026-09-11 11:40 ` Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 5/6] drm/panthor: Add support for entering and exiting protected mode Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 6/6] drm/panthor: Expose protected rendering features Ketil Johnsen
5 siblings, 0 replies; 11+ messages in thread
From: Ketil Johnsen @ 2026-09-11 11:40 UTC (permalink / raw)
To: Boris Brezillon, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Daniel Almeida, Alice Ryhl
Cc: dri-devel, linux-kernel, Ketil Johnsen
The PROTM suspend buffer is only needed if the group is going to
use PROTM in the first place, so let's not assume we need one until
we're being asked to.
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Steven Price <steven.price@arm.com>
Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Signed-off-by: Ketil Johnsen <ketil.johnsen@arm.com>
---
drivers/gpu/drm/panthor/panthor_sched.c | 14 ++++----------
1 file changed, 4 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index e02062e8443ec..0c8ea07fc7b9d 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -3636,9 +3636,11 @@ static void add_group_kbo_sizes(struct panthor_device *ptdev,
if (drm_WARN_ON(&ptdev->base, ptdev != group->ptdev))
return;
- group->fdinfo.kbo_sizes += group->suspend_buf->obj->size;
- group->fdinfo.kbo_sizes += group->protm_suspend_buf->obj->size;
group->fdinfo.kbo_sizes += group->syncobjs->obj->size;
+ group->fdinfo.kbo_sizes += group->suspend_buf->obj->size;
+
+ if (group->protm_suspend_buf)
+ group->fdinfo.kbo_sizes += group->protm_suspend_buf->obj->size;
for (i = 0; i < group->queue_count; i++) {
queue = group->queues[i];
@@ -3716,14 +3718,6 @@ int panthor_group_create(struct drm_file *file,
goto err_put_group;
}
- suspend_size = csg_iface->control->protm_suspend_size;
- group->protm_suspend_buf = panthor_fw_alloc_suspend_buf_mem(ptdev, suspend_size);
- if (IS_ERR(group->protm_suspend_buf)) {
- ret = PTR_ERR(group->protm_suspend_buf);
- group->protm_suspend_buf = NULL;
- goto err_put_group;
- }
-
group->syncobjs = panthor_kernel_bo_create(ptdev, group->vm,
group_args->queues.count *
sizeof(struct panthor_syncobj_64b),
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 5/6] drm/panthor: Add support for entering and exiting protected mode
2026-09-11 11:40 [PATCH v3 0/6] drm/panthor: Protected mode support for Mali CSF GPUs Ketil Johnsen
` (3 preceding siblings ...)
2026-09-11 11:40 ` [PATCH v3 4/6] drm/panthor: Don't allocate protm_suspend_buf Ketil Johnsen
@ 2026-09-11 11:40 ` Ketil Johnsen
2026-09-11 11:56 ` sashiko-bot
2026-09-11 11:40 ` [PATCH v3 6/6] drm/panthor: Expose protected rendering features Ketil Johnsen
5 siblings, 1 reply; 11+ messages in thread
From: Ketil Johnsen @ 2026-09-11 11:40 UTC (permalink / raw)
To: Boris Brezillon, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Daniel Almeida, Alice Ryhl
Cc: dri-devel, linux-kernel, Florent Tomasin, Paul Toadere,
Samuel Percival, Ketil Johnsen
From: Florent Tomasin <florent.tomasin@arm.com>
This patch modifies the Panthor driver code to allow handling
of the GPU HW protected mode enter and exit.
The logic added by this patch includes:
- the mechanisms needed for entering and exiting protected mode.
- the handling of protected mode IRQs and FW interactions.
- the scheduler changes needed to decide when to enter
protected mode based on CSG scheduling.
- GPU fault handling during protected mode execution.
Note that the submission of a protected mode jobs are done
from the user space.
The following is a summary of how protected mode is entered
and exited:
- When the GPU detects a protected mode job needs to be
executed, an IRQ is sent to the CPU to notify the kernel
driver that the job is blocked until the GPU has entered
protected mode. The entering of protected mode is controlled
by the kernel driver.
- The Mali Panthor CSF driver will schedule a tick and evaluate
which CS in the CSG to schedule on slot needs protected mode.
If the priority of the CSG is not sufficiently high, the
protected mode job will not progress until the CSG is
scheduled at top priority.
- The Panthor scheduler notifies the GPU that the blocked
protected jobs will soon be able to progress.
- Once all CSG and CS slots are updated, the scheduler
requests the GPU to enter protected mode and waits for
it to be acknowledged.
- If successful, all protected mode jobs will resume execution
while normal mode jobs block until the GPU exits
protected mode, or the kernel driver rotates the CSGs
and forces the GPU to exit protected mode.
- If unsuccessful, the scheduler will request a GPU reset.
- Faults during protected mode are reported GPU wide, and not as
CSG/CS errors. We allow only one CSG to run in protected mode at a
time so we know which CSG to blame for the fault.
- All faults during protected mode are handled with a GPU reset.
- When a protected mode job is suspended as a result of
the CSGs rotation, the GPU will send an IRQ to the CPU
to notify that the protected mode job needs to resume.
This sequence will continue so long the user space is
submitting protected mode jobs.
Signed-off-by: Florent Tomasin <florent.tomasin@arm.com>
Co-developed-by: Paul Toadere <paul.toadere@arm.com>
Signed-off-by: Paul Toadere <paul.toadere@arm.com>
Co-developed-by: Samuel Percival <samuel.percival@arm.com>
Signed-off-by: Samuel Percival <samuel.percival@arm.com>
Co-developed-by: Ketil Johnsen <ketil.johnsen@arm.com>
Signed-off-by: Ketil Johnsen <ketil.johnsen@arm.com>
---
v3:
- Rebase
- Required changes due to new IRQ handling (events_lock).
- Required changes due to memory reclaim (more places to sync with protm).
- Count number of enter and exits to and from protected mode.
- Added helper function wait_protm_enter() to check protm enter condition, using
the counters mentioned above.
- Tweaks to timeouts.
- Removed disable/enable of GPU_IRQ_PROTM_FAULT. No longer recall what issue it
was supposed to fix.
- protm_fault changed to atomic.
- Moved sync with protm out from panthor_vm_lock_region() and
panthor_vm_unlock_region().
- Avoids special dealing with as.slots_lock and GPU reset.
- Block and unblock of protm in same function, so easier to see.
- We no longer need to add panthor_vm_expand_locked_region() (different patch).
- The downside is that we might block protm mode more than necessary.
- Added panthor_sched_protm_try_block() used by memory reclaim case.
- protm_pending_queues made atomic (following the same change for fatal_queues).
- Only clear bits from protm_pending_queues when we ACK CS_PROTM_PENDING.
- Clear any pending CS_PROTM_PENDING on slot reset.
- Ack all CS_PROTM_PENDING in tick_ctx_handle_protm_group(), no matter the value
of protm_pending_queues.
- Inlined helper function panthor_sched_protm_enter().
v2:
- Heavily reworked, although conceptually similar to v1.
---
drivers/gpu/drm/panthor/panthor_device.c | 1 +
drivers/gpu/drm/panthor/panthor_device.h | 31 ++
drivers/gpu/drm/panthor/panthor_fw.c | 92 +++++-
drivers/gpu/drm/panthor/panthor_fw.h | 4 +
drivers/gpu/drm/panthor/panthor_gpu.c | 47 ++-
drivers/gpu/drm/panthor/panthor_gpu.h | 4 +
drivers/gpu/drm/panthor/panthor_mmu.c | 24 +-
drivers/gpu/drm/panthor/panthor_sched.c | 350 +++++++++++++++++++++--
drivers/gpu/drm/panthor/panthor_sched.h | 4 +
9 files changed, 533 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c
index 9687c59de3505..0a41f69c473c3 100644
--- a/drivers/gpu/drm/panthor/panthor_device.c
+++ b/drivers/gpu/drm/panthor/panthor_device.c
@@ -172,6 +172,7 @@ int panthor_device_init(struct panthor_device *ptdev)
ptdev->soc_data = of_device_get_match_data(ptdev->base.dev);
+ init_rwsem(&ptdev->protm.lock);
init_completion(&ptdev->unplug.done);
ret = drmm_mutex_init(&ptdev->base, &ptdev->unplug.lock);
if (ret)
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index b55a3f9edd414..f1b7f51f5ae1e 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -336,6 +336,37 @@ struct panthor_device {
struct list_head node;
} gems;
#endif
+ /** @protm: Protected mode related data. */
+ struct {
+ /**
+ * @lock: Lock to prevent MMU operations during protected mode.
+ *
+ * The MMU HW will silently ignore commands issued when the
+ * GPU is in protected mode. It is important that we handle this
+ * for some of the MMU HW interactions.
+ *
+ * Code which interacts with the MMU, typically by calling
+ * panthor_vm_lock_region(), should therefore ensure the
+ * scheduler is not in and will not enter protected mode first.
+ * This is done by calling either
+ * - panthor_sched_protm_block(), or
+ * - panthor_sched_protm_try_block()
+ *
+ * Once the MMU operations have completed, call
+ * panthor_sched_protm_unblock() to tell the scheduler that
+ * it is safe to enter protected mode again.
+ *
+ * The block/unblock for MMU operations take this as reader.
+ * The scheduler holds this as writer when switching into protm.
+ */
+ struct rw_semaphore lock;
+
+ /** @protm_enter_count: Number of times entered protm. */
+ atomic64_t protm_enter_count;
+
+ /** @protm_exit_count: Number of times exited protm. */
+ atomic64_t protm_exit_count;
+ } protm;
};
struct panthor_gpu_usage {
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 5f9f7a92c56a8..96770ce34da84 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -1067,7 +1067,9 @@ static void panthor_fw_init_global_iface(struct panthor_device *ptdev)
GLB_CFG_PROGRESS_TIMER |
GLB_CFG_POWEROFF_TIMER |
GLB_IDLE_EN |
- GLB_IDLE;
+ GLB_IDLE |
+ GLB_PROTM_ENTER |
+ GLB_PROTM_EXIT;
if (panthor_fw_has_glb_state(ptdev))
glb_iface->input->ack_irq_mask |= GLB_STATE_MASK;
@@ -1281,6 +1283,9 @@ int panthor_fw_post_reset(struct panthor_device *ptdev)
return ret;
}
+ atomic64_set(&ptdev->protm.protm_enter_count, 0);
+ atomic64_set(&ptdev->protm.protm_exit_count, 0);
+
/* We must re-initialize the global interface even on fast-reset. */
panthor_fw_init_global_iface(ptdev);
return 0;
@@ -1476,6 +1481,91 @@ static void panthor_fw_ping_work(struct work_struct *work)
}
}
+static bool wait_protm_enter(struct panthor_device *ptdev,
+ long long enter_count)
+{
+ return (panthor_gpu_status(ptdev) & GPU_STATUS_PROTM_ACTIVE) ||
+ (atomic64_read(&ptdev->protm.protm_exit_count) >= enter_count);
+}
+
+int panthor_fw_protm_enter(struct panthor_device *ptdev)
+{
+ struct panthor_fw_global_iface *glb_iface =
+ panthor_fw_get_glb_iface(ptdev);
+ u32 acked;
+ int ret;
+ long long enter_count;
+
+ /* Restart the watchdog timer, so it doesn't hit immediately
+ * after entering protected mode, since this will cause GPU
+ * to exit protected mode to respond to the ping request.
+ */
+ mod_delayed_work(ptdev->reset.wq, &ptdev->fw->watchdog.ping_work,
+ msecs_to_jiffies(PING_INTERVAL_MS));
+
+ panthor_fw_toggle_reqs(glb_iface, req, ack, GLB_PROTM_ENTER);
+ panthor_fw_ring_doorbell(ptdev, CSF_GLB_DOORBELL_ID);
+
+ ret = panthor_fw_glb_wait_acks(ptdev, GLB_PROTM_ENTER, &acked, 250);
+ if (ret) {
+ drm_err(&ptdev->base,
+ "Wait for FW protected mode acknowledge timed out");
+ return ret;
+ }
+
+ enter_count = atomic64_inc_return(&ptdev->protm.protm_enter_count);
+
+ /* Poll for the entry of protected mode.
+ * It is possible that GPU_STATUS_PROTM_ACTIVE is set and cleared
+ * before we check it below, so we must also check for GLB_PROTM_EXIT.
+ * GLB_PROTM_EXIT can not be checked directly, because this could also
+ * be handled and clear before we check below. We count number of
+ * protm enters and exits to safely handle that case.
+ */
+ ret = wait_event_timeout(ptdev->fw->req_waitqueue,
+ wait_protm_enter(ptdev, enter_count),
+ msecs_to_jiffies(500));
+ if (!ret) {
+ drm_err(&ptdev->base,
+ "Wait for GPU protected mode enter timed out");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+int panthor_fw_protm_exit_wait(struct panthor_device *ptdev, u32 timeout_ms)
+{
+ int ret;
+
+ ret = wait_event_timeout(ptdev->fw->req_waitqueue,
+ !(panthor_gpu_status(ptdev) &
+ GPU_STATUS_PROTM_ACTIVE),
+ msecs_to_jiffies(timeout_ms));
+ if (!ret)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+int panthor_fw_protm_exit(struct panthor_device *ptdev, u32 timeout_ms)
+{
+ struct panthor_fw_global_iface *glb_iface =
+ panthor_fw_get_glb_iface(ptdev);
+ int ret;
+
+ /* Send PING request to force an exit */
+ panthor_fw_toggle_reqs(glb_iface, req, ack, GLB_PING);
+ panthor_fw_ring_doorbell(ptdev, CSF_GLB_DOORBELL_ID);
+
+ ret = panthor_fw_protm_exit_wait(ptdev, timeout_ms);
+ if (ret)
+ drm_err(&ptdev->base,
+ "Wait for GPU protected mode exit timed out");
+
+ return ret;
+}
+
/**
* panthor_fw_init() - Initialize FW related data.
* @ptdev: Device.
diff --git a/drivers/gpu/drm/panthor/panthor_fw.h b/drivers/gpu/drm/panthor/panthor_fw.h
index a99a9b6f4825c..4eda8f8e714c1 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.h
+++ b/drivers/gpu/drm/panthor/panthor_fw.h
@@ -529,4 +529,8 @@ static inline int panthor_fw_resume(struct panthor_device *ptdev)
int panthor_fw_init(struct panthor_device *ptdev);
void panthor_fw_unplug(struct panthor_device *ptdev);
+int panthor_fw_protm_enter(struct panthor_device *ptdev);
+int panthor_fw_protm_exit(struct panthor_device *ptdev, u32 timeout_ms);
+int panthor_fw_protm_exit_wait(struct panthor_device *ptdev, u32 timeout_ms);
+
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
index a383b04f101ed..27617f3a72394 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.c
+++ b/drivers/gpu/drm/panthor/panthor_gpu.c
@@ -46,6 +46,9 @@ struct panthor_gpu {
/** @cache_flush_lock: Lock to serialize cache flushes */
struct mutex cache_flush_lock;
+
+ /** @protm_fault: True if a GPU_IRQ_PROTM_FAULT has been raised */
+ atomic_t protm_fault;
};
#define GPU_INTERRUPTS_MASK \
@@ -91,6 +94,34 @@ static void panthor_gpu_irq_handler(struct panthor_irq *pirq, u32 status)
struct panthor_device *ptdev = pirq->ptdev;
struct panthor_gpu *gpu = ptdev->gpu;
+ if (status & GPU_IRQ_PROTM_FAULT) {
+ /* Make a note of this fault before we clear the interrupt.
+ * This ensures panthor_gpu_protm_fault_pending() can always
+ * give an accurate answer.
+ *
+ * There is a race we need to handle between two interrupts,
+ * this GPU_IRQ_PROTM_FAULT and JOB_INT_GLOBAL_IF with the
+ * GLB_PROTM_EXIT event.
+ *
+ * Although GPU_IRQ_PROTM_FAULT is always raised first,
+ * processing of GLB_PROTM_EXIT could still execute first.
+ * The handling of GLB_PROTM_EXIT MUST know if a
+ * GPU_IRQ_PROTM_FAULT has been raised or not, otherwise it
+ * could incorrectly think everything is fine and resume
+ * with normal scheduling to early.
+ *
+ * We still need to do fault handling (reset) here as well,
+ * because some failures during protected mode do not
+ * automatically exit protected mode (no GLB_PROTM_EXIT).
+ * This means there is a slim chance we do two GPU resets
+ * instead of just one. This is not ideal, but should be safe.
+ */
+ atomic_set(&gpu->protm_fault, 1);
+
+ drm_warn(&ptdev->base, "GPU Fault in protected mode\n");
+ panthor_device_schedule_reset(ptdev);
+ }
+
gpu_write(gpu->irq.iomem, INT_CLEAR, status);
if (tracepoint_enabled(gpu_power_status) && (status & GPU_POWER_INTERRUPTS_MASK))
@@ -107,8 +138,6 @@ static void panthor_gpu_irq_handler(struct panthor_irq *pirq, u32 status)
fault_status, panthor_exception_name(ptdev, fault_status & 0xFF),
address);
}
- if (status & GPU_IRQ_PROTM_FAULT)
- drm_warn(&ptdev->base, "GPU Fault in protected mode\n");
spin_lock(&ptdev->gpu->reqs_lock);
if (status & ptdev->gpu->pending_reqs) {
@@ -123,6 +152,13 @@ static irqreturn_t panthor_gpu_irq_threaded_handler(int irq, void *data)
return panthor_irq_default_threaded_handler(data, panthor_gpu_irq_handler);
}
+bool panthor_gpu_protm_fault_pending(struct panthor_device *ptdev)
+{
+ return atomic_read(&ptdev->gpu->protm_fault) ||
+ gpu_read(ptdev->gpu->irq.iomem, INT_RAWSTAT) &
+ GPU_IRQ_PROTM_FAULT;
+}
+
/**
* panthor_gpu_unplug() - Called when the GPU is unplugged.
* @ptdev: Device to unplug.
@@ -404,6 +440,8 @@ int panthor_gpu_soft_reset(struct panthor_device *ptdev)
struct panthor_gpu *gpu = ptdev->gpu;
bool timedout = false;
+ atomic_set(&ptdev->gpu->protm_fault, 0);
+
scoped_guard(spinlock, &ptdev->gpu->reqs_lock) {
if (!drm_WARN_ON(&ptdev->base,
ptdev->gpu->pending_reqs & GPU_IRQ_RESET_COMPLETED)) {
@@ -508,3 +546,8 @@ int panthor_gpu_coherency_init(struct panthor_device *ptdev)
drm_err(&ptdev->base, "Coherency not supported by the device");
return -ENOTSUPP;
}
+
+u32 panthor_gpu_status(struct panthor_device *ptdev)
+{
+ return gpu_read(ptdev->gpu->iomem, GPU_STATUS);
+}
diff --git a/drivers/gpu/drm/panthor/panthor_gpu.h b/drivers/gpu/drm/panthor/panthor_gpu.h
index f615feb056094..00ec9a8c5d7fd 100644
--- a/drivers/gpu/drm/panthor/panthor_gpu.h
+++ b/drivers/gpu/drm/panthor/panthor_gpu.h
@@ -60,4 +60,8 @@ u64 panthor_gpu_get_cycle_count(struct panthor_device *ptdev);
int panthor_gpu_coherency_init(struct panthor_device *ptdev);
+u32 panthor_gpu_status(struct panthor_device *ptdev);
+
+bool panthor_gpu_protm_fault_pending(struct panthor_device *ptdev);
+
#endif
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index 3e5f20768d545..1e1a158aa12ed 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -2505,6 +2505,11 @@ int panthor_vm_evict_bo_mappings_locked(struct panthor_gem_object *bo)
if (!mutex_trylock(&vm->op_lock))
return -EDEADLK;
+ if (panthor_sched_protm_try_block(vm->ptdev)) {
+ mutex_unlock(&vm->op_lock);
+ return -EDEADLK;
+ }
+
/* It can be that the vm_bo was already evicted but a new
* mapping pointing to this BO got created in the meantime,
* thus turning the vm_bo in partially evicted state. In that case
@@ -2540,6 +2545,7 @@ int panthor_vm_evict_bo_mappings_locked(struct panthor_gem_object *bo)
vma->evicted = true;
}
+ panthor_sched_protm_unblock(vm->ptdev);
mutex_unlock(&vm->op_lock);
if (ret)
@@ -2612,6 +2618,10 @@ static int remap_evicted_vma(struct drm_gpuvm_bo *vm_bo,
}
if (found) {
+ ret = panthor_sched_protm_block(vm->ptdev);
+ if (ret)
+ goto out_unlock;
+
vm->op_ctx = op_ctx;
ret = panthor_vm_lock_region(vm, evicted_vma->base.va.addr,
evicted_vma->base.va.range);
@@ -2633,9 +2643,12 @@ static int remap_evicted_vma(struct drm_gpuvm_bo *vm_bo,
panthor_vm_unlock_region(vm);
}
+ panthor_sched_protm_unblock(vm->ptdev);
+
vm->op_ctx = NULL;
}
+out_unlock:
mutex_unlock(&vm->op_lock);
out_cleanup:
@@ -2726,9 +2739,13 @@ panthor_vm_exec_op(struct panthor_vm *vm, struct panthor_vm_op_ctx *op,
mutex_lock(&vm->op_lock);
vm->op_ctx = op;
+ ret = panthor_sched_protm_block(vm->ptdev);
+ if (ret)
+ goto out_unlock;
+
ret = panthor_vm_lock_region(vm, op->va.addr, op->va.range);
if (ret)
- goto out;
+ goto out_unblock;
switch (op_type) {
case DRM_PANTHOR_VM_BIND_OP_TYPE_MAP: {
@@ -2759,10 +2776,13 @@ panthor_vm_exec_op(struct panthor_vm *vm, struct panthor_vm_op_ctx *op,
panthor_vm_unlock_region(vm);
-out:
+out_unblock:
+ panthor_sched_protm_unblock(vm->ptdev);
+
if (ret && flag_vm_unusable_on_failure)
panthor_vm_declare_unusable(vm);
+out_unlock:
vm->op_ctx = NULL;
mutex_unlock(&vm->op_lock);
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 0c8ea07fc7b9d..1fe77e5c41995 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -306,6 +306,17 @@ struct panthor_scheduler {
*/
struct list_head stopped_groups;
} reset;
+
+ /** @protm: Protected mode related fields. */
+ struct {
+ /**
+ * @active_group: The active protected group.
+ *
+ * We only allow one protected group to run at the same time,
+ * as it makes it easier to handle faults in protected mode.
+ */
+ struct panthor_group *active_group;
+ } protm;
};
/**
@@ -570,6 +581,16 @@ struct panthor_group {
*/
atomic_t fatal_queues;
+ /**
+ * @protm_pending_queues: Bitmask reflecting the queues that have raised
+ * a CS_PROTM_PENDING.
+ *
+ * The GPU will set the bit associated to the queue pending protected
+ * mode when a PROT_REGION command is executing or when trying to resume
+ * previously suspended protected mode jobs.
+ */
+ atomic_t protm_pending_queues;
+
/** @tiler_oom: Mask of queues that have a tiler OOM event to process. */
atomic_t tiler_oom;
@@ -1149,12 +1170,14 @@ cs_slot_reset_locked(struct panthor_device *ptdev, u32 csg_id, u32 cs_id)
struct panthor_fw_cs_iface *cs_iface = panthor_fw_get_cs_iface(ptdev, csg_id, cs_id);
struct panthor_group *group = ptdev->scheduler->csg_slots[csg_id].group;
struct panthor_queue *queue = group->queues[cs_id];
+ u32 val, mask;
lockdep_assert_held(&ptdev->scheduler->lock);
- panthor_fw_update_reqs(cs_iface, req,
- CS_STATE_STOP,
- CS_STATE_MASK);
+ val = CS_STATE_STOP | (cs_iface->output->ack & CS_PROTM_PENDING);
+ mask = CS_STATE_MASK | CS_PROTM_PENDING;
+
+ panthor_fw_update_reqs(cs_iface, req, val, mask);
queue_suspend_timeout(queue);
@@ -1393,6 +1416,27 @@ csg_slot_prog_locked(struct panthor_device *ptdev, u32 csg_id, u32 priority)
return 0;
}
+static void
+cs_slot_process_protm_pending_event_locked(struct panthor_device *ptdev,
+ u32 csg_id, u32 cs_id)
+{
+ struct panthor_scheduler *sched = ptdev->scheduler;
+ struct panthor_csg_slot *csg_slot = &sched->csg_slots[csg_id];
+ struct panthor_group *group = csg_slot->group;
+
+ lockdep_assert_held(&sched->events_lock);
+
+ if (!group)
+ return;
+
+ /* Do not allow user space work to switch into protected mode, as we
+ * do not fully support this quite yet.
+ */
+ atomic_or(BIT(cs_id), &group->fatal_queues);
+
+ sched_queue_delayed_work(sched, tick, 0);
+}
+
static void
cs_slot_process_fatal_event_locked(struct panthor_device *ptdev,
u32 csg_id, u32 cs_id)
@@ -1641,6 +1685,10 @@ static bool cs_slot_process_irq_locked(struct panthor_device *ptdev,
if (events & CS_TILER_OOM)
cs_slot_process_tiler_oom_event_locked(ptdev, csg_id, cs_id);
+ if (events & CS_PROTM_PENDING)
+ cs_slot_process_protm_pending_event_locked(ptdev, csg_id,
+ cs_id);
+
/* We don't acknowledge the TILER_OOM event since its handling is
* deferred to a separate work.
*/
@@ -1855,6 +1903,38 @@ static void sched_process_idle_event_locked(struct panthor_device *ptdev)
sched_queue_delayed_work(ptdev->scheduler, tick, 0);
}
+static void sched_process_protm_exit_event_locked(struct panthor_device *ptdev)
+{
+ struct panthor_fw_global_iface *glb_iface =
+ panthor_fw_get_glb_iface(ptdev);
+ struct panthor_scheduler *sched = ptdev->scheduler;
+
+ lockdep_assert_held(&sched->events_lock);
+
+ atomic64_inc(&ptdev->protm.protm_exit_count);
+
+ /* Acknowledge the protm exit */
+ panthor_fw_update_reqs(glb_iface, req, glb_iface->output->ack,
+ GLB_PROTM_EXIT);
+
+ /* If there are pending fault from protected mode execution, then early
+ * out here. The GPU_IRQ_PROTM_FAULT handling will trigger the propper
+ * error recovery via a GPU reset.
+ */
+ if (panthor_gpu_protm_fault_pending(ptdev))
+ return;
+
+ /* Protected mode exited successfully. Clear protm.active_group so that
+ * tick_work() is unblocked to schedule new work.
+ */
+ if (sched->protm.active_group) {
+ group_put(sched->protm.active_group);
+ sched->protm.active_group = NULL;
+ }
+
+ sched_queue_delayed_work(sched, tick, 0);
+}
+
/**
* sched_process_global_irq_locked() - Process the scheduling part of a global IRQ
* @ptdev: Device.
@@ -1870,6 +1950,9 @@ static void sched_process_global_irq_locked(struct panthor_device *ptdev)
ack = READ_ONCE(glb_iface->output->ack);
evts = (req ^ ack) & GLB_EVT_MASK;
+ if (evts & GLB_PROTM_EXIT)
+ sched_process_protm_exit_event_locked(ptdev);
+
if (evts & GLB_IDLE)
sched_process_idle_event_locked(ptdev);
}
@@ -1881,22 +1964,22 @@ static void sched_process_global_irq_locked(struct panthor_device *ptdev)
*/
void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events)
{
+ u32 csg_events = events & ~JOB_INT_GLOBAL_IF;
+
if (!ptdev->scheduler)
return;
guard(spinlock)(&ptdev->scheduler->events_lock);
- if (events & JOB_INT_GLOBAL_IF) {
- sched_process_global_irq_locked(ptdev);
- events &= ~JOB_INT_GLOBAL_IF;
- }
-
- while (events) {
- u32 csg_id = ffs(events) - 1;
+ while (csg_events) {
+ u32 csg_id = ffs(csg_events) - 1;
sched_process_csg_irq_locked(ptdev, csg_id);
- events &= ~BIT(csg_id);
+ csg_events &= ~BIT(csg_id);
}
+
+ if (events & JOB_INT_GLOBAL_IF)
+ sched_process_global_irq_locked(ptdev);
}
/**
@@ -1982,6 +2065,69 @@ group_unbind_locked(struct panthor_group *group)
return 0;
}
+static void handle_protm_fault(struct panthor_device *ptdev)
+{
+ struct panthor_scheduler *sched = ptdev->scheduler;
+ u32 csg_id;
+ struct panthor_group *protm_group;
+
+ guard(mutex)(&sched->lock);
+
+ protm_group = sched->protm.active_group;
+
+ if (!protm_group || !panthor_gpu_protm_fault_pending(ptdev))
+ return;
+
+ atomic_set(&protm_group->fatal_queues,
+ GENMASK(protm_group->queue_count - 1, 0));
+
+ /* Different kinds of faults during protected mode can give different
+ * behavior/state.
+ * Case 1) The fault keeps the GPU in protected mode.
+ * In this case, the request to exit protected mode below will
+ * fail and we need to take some further action.
+ * Case 2) The fault do not keep the GPU in protected mode.
+ * In this case, the request to exit protected
+ * mode below will succeed, and we don't need to take any
+ * further action right here.
+ */
+ if (!panthor_fw_protm_exit(ptdev, 500))
+ return;
+
+ /* GPU failed to exit protected mode.
+ * Mark all CSGs as suspended and unbind them, so that they are
+ * unaffected by the GPU reset itself.
+ * We can not suspend the groups in this case, because we are stuck
+ * in protected mode. That is also the reason it is safe to unbind
+ * without suspending first (the groups are already "suspended").
+ * The failing protected group will be scheduled for termination.
+ */
+
+ for (csg_id = 0; csg_id < sched->csg_slot_count; csg_id++) {
+ struct panthor_group *group = sched->csg_slots[csg_id].group;
+
+ if (!group)
+ continue;
+
+ group_get(group);
+
+ group->state = PANTHOR_CS_GROUP_SUSPENDED;
+ group_unbind_locked(group);
+
+ drm_WARN_ON(&group->ptdev->base, !list_empty(&group->run_node));
+
+ if (group_can_run(group)) {
+ list_add(&group->run_node,
+ &sched->groups.idle[group->priority]);
+ } else {
+ list_del_init(&group->wait_node);
+ group_queue_work(group, term);
+ }
+
+ group_put(group);
+ }
+}
+
static const char *fence_get_driver_name(struct dma_fence *fence)
{
return "panthor";
@@ -2011,6 +2157,12 @@ static void csgs_upd_ctx_init(struct panthor_csg_slots_upd_ctx *ctx)
memset(ctx, 0, sizeof(*ctx));
}
+static void csgs_upd_ctx_ring_doorbell(struct panthor_csg_slots_upd_ctx *ctx,
+ u32 csg_id)
+{
+ ctx->update_mask |= BIT(csg_id);
+}
+
static void csgs_upd_ctx_queue_reqs(struct panthor_device *ptdev,
struct panthor_csg_slots_upd_ctx *ctx,
u32 csg_id, u32 value, u32 mask)
@@ -2021,7 +2173,8 @@ static void csgs_upd_ctx_queue_reqs(struct panthor_device *ptdev,
ctx->requests[csg_id].value = (ctx->requests[csg_id].value & ~mask) | (value & mask);
ctx->requests[csg_id].mask |= mask;
- ctx->update_mask |= BIT(csg_id);
+
+ csgs_upd_ctx_ring_doorbell(ctx, csg_id);
}
static int csgs_upd_ctx_apply_locked(struct panthor_device *ptdev,
@@ -2038,8 +2191,12 @@ static int csgs_upd_ctx_apply_locked(struct panthor_device *ptdev,
while (update_slots) {
struct panthor_fw_csg_iface *csg_iface;
u32 csg_id = ffs(update_slots) - 1;
+ u32 req_mask = ctx->requests[csg_id].mask;
update_slots &= ~BIT(csg_id);
+ if (!req_mask)
+ continue;
+
csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id);
panthor_fw_update_reqs(csg_iface, req,
ctx->requests[csg_id].value,
@@ -2056,6 +2213,9 @@ static int csgs_upd_ctx_apply_locked(struct panthor_device *ptdev,
int ret;
update_slots &= ~BIT(csg_id);
+ if (!req_mask)
+ continue;
+
csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id);
ret = panthor_fw_csg_wait_acks(ptdev, csg_id, req_mask, &acked, 100);
@@ -2092,6 +2252,7 @@ struct panthor_sched_tick_ctx {
bool immediate_tick;
bool stop_tick;
u32 csg_upd_failed_mask;
+ struct panthor_group *protm_group;
};
static bool
@@ -2133,6 +2294,11 @@ tick_ctx_pick_groups_from_list(const struct panthor_scheduler *sched,
if (!owned_by_tick_ctx)
group_get(group);
+ /* Only the first pick is allowed to request switch to protm */
+ if (ctx->group_count == 0 &&
+ atomic_read(&group->protm_pending_queues))
+ ctx->protm_group = group;
+
ctx->group_count++;
/* If we have more than one active group with the same priority,
@@ -2291,6 +2457,48 @@ static void group_term_work(struct work_struct *work)
group_put(group);
}
+int panthor_sched_protm_block(struct panthor_device *ptdev)
+{
+ int ret;
+
+ down_read(&ptdev->protm.lock);
+
+ /* First, wait a little bit for FW to exit protected mode on its own.
+ * Only if that fails do we request a protected mode exit.
+ */
+
+ ret = panthor_fw_protm_exit_wait(ptdev, 5);
+ if (ret) {
+ ret = panthor_fw_protm_exit(ptdev, 2000);
+ if (ret)
+ up_read(&ptdev->protm.lock);
+ }
+
+ return ret;
+}
+
+int panthor_sched_protm_try_block(struct panthor_device *ptdev)
+{
+ int ret;
+
+ ret = down_read_trylock(&ptdev->protm.lock);
+ if (ret) {
+ if (panthor_gpu_status(ptdev) & GPU_STATUS_PROTM_ACTIVE) {
+ up_read(&ptdev->protm.lock);
+ return -EAGAIN;
+ }
+
+ return 0;
+ }
+
+ return -EAGAIN;
+}
+
+void panthor_sched_protm_unblock(struct panthor_device *ptdev)
+{
+ up_read(&ptdev->protm.lock);
+}
+
static void
tick_ctx_cleanup(struct panthor_scheduler *sched,
struct panthor_sched_tick_ctx *ctx)
@@ -2414,6 +2622,46 @@ tick_ctx_schedule_group(struct panthor_scheduler *sched,
return 0;
}
+static void
+tick_ctx_handle_protm_group(struct panthor_scheduler *sched,
+ struct panthor_csg_slots_upd_ctx *upd_ctx,
+ struct panthor_group *group)
+{
+ struct panthor_device *ptdev = sched->ptdev;
+ struct panthor_fw_csg_iface *csg_iface =
+ panthor_fw_get_csg_iface(ptdev, group->csg_id);
+ u32 q;
+ u32 cs_acked = 0;
+
+ if (drm_WARN_ON(&ptdev->base, group->csg_id < 0))
+ return;
+
+ for (q = 0; q < group->queue_count; q++) {
+ struct panthor_fw_cs_iface *cs_iface =
+ panthor_fw_get_cs_iface(ptdev, group->csg_id, q);
+
+ /* Ack any pending CS_PROTM_PENDING so it can run in protm */
+ if ((cs_iface->output->ack ^ cs_iface->input->req) &
+ CS_PROTM_PENDING) {
+ drm_WARN_ON(
+ &ptdev->base,
+ !(atomic_read(&group->protm_pending_queues) &
+ BIT(q)));
+
+ panthor_fw_update_reqs(cs_iface, req,
+ cs_iface->output->ack,
+ CS_PROTM_PENDING);
+ cs_acked |= BIT(q);
+ }
+ }
+
+ /* Clear only the ones we acked */
+ atomic_andnot(cs_acked, &group->protm_pending_queues);
+
+ panthor_fw_toggle_reqs(csg_iface, doorbell_req, doorbell_ack, cs_acked);
+ csgs_upd_ctx_ring_doorbell(upd_ctx, group->csg_id);
+}
+
static void
tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *ctx)
{
@@ -2483,6 +2731,9 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c
}
}
+ if (ctx->protm_group)
+ tick_ctx_handle_protm_group(sched, &upd_ctx, ctx->protm_group);
+
ret = csgs_upd_ctx_apply_locked(ptdev, &upd_ctx);
if (ret) {
panthor_device_schedule_reset(ptdev);
@@ -2490,6 +2741,24 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c
return;
}
+ if (ctx->protm_group) {
+ if (drm_WARN_ON(&ptdev->base, sched->protm.active_group))
+ group_put(sched->protm.active_group);
+
+ sched->protm.active_group = ctx->protm_group;
+ group_get(sched->protm.active_group);
+
+ down_write(&ptdev->protm.lock);
+
+ ret = panthor_fw_protm_enter(ptdev);
+ if (ret) {
+ panthor_device_schedule_reset(ptdev);
+ ctx->csg_upd_failed_mask = U32_MAX;
+ }
+
+ up_write(&ptdev->protm.lock);
+ }
+
for (prio = PANTHOR_CSG_PRIORITY_COUNT - 1; prio >= 0; prio--) {
list_for_each_entry_safe(group, tmp, &ctx->groups[prio], run_node) {
list_del_init(&group->run_node);
@@ -2579,6 +2848,23 @@ static void tick_work(struct work_struct *work)
if (panthor_device_reset_is_pending(sched->ptdev))
goto out_unlock;
+ if (sched->protm.active_group) {
+ bool rt_groups_waiting = !list_empty(
+ &sched->groups.runnable[PANTHOR_CSG_PRIORITY_RT]);
+
+ if (full_tick || rt_groups_waiting) {
+ /* We allow preemption in this case, but we must
+ * ensure we are fully out of protected mode first.
+ * We rely on the GLB_PROTM_EXIT (or error recovery)
+ * to get a new tick.
+ */
+ if (panthor_fw_protm_exit(ptdev, 500))
+ panthor_device_schedule_reset(ptdev);
+ }
+
+ goto out_unlock;
+ }
+
tick_ctx_init(sched, &ctx);
if (ctx.csg_upd_failed_mask)
goto out_cleanup_ctx;
@@ -2631,13 +2917,32 @@ static void tick_work(struct work_struct *work)
}
/* If we have free CSG slots left, pick idle groups */
- for (prio = PANTHOR_CSG_PRIORITY_COUNT - 1;
- prio >= 0 && !tick_ctx_is_full(sched, &ctx);
- prio--) {
- /* Check the old_group queue first to avoid reprogramming the slots */
- tick_ctx_pick_groups_from_list(sched, &ctx, &ctx.old_groups[prio], false, true);
- tick_ctx_pick_groups_from_list(sched, &ctx, &sched->groups.idle[prio],
- false, false);
+ if (ctx.protm_group) {
+ /* Pick only idle groups with equal or lower priority than the
+ * group triggering protected mode. Do not bother picking
+ * unscheduled idle groups.
+ */
+ for (prio = ctx.protm_group->priority;
+ prio >= 0 && !tick_ctx_is_full(sched, &ctx); prio--)
+ tick_ctx_pick_groups_from_list(sched, &ctx,
+ &ctx.old_groups[prio],
+ false, true);
+ } else {
+ /* No switch to protected, just pick any idle group according
+ * to priority
+ */
+ for (prio = PANTHOR_CSG_PRIORITY_COUNT - 1;
+ prio >= 0 && !tick_ctx_is_full(sched, &ctx); prio--) {
+ /* Check the old_group queue first to avoid
+ * reprogramming the slots
+ */
+ tick_ctx_pick_groups_from_list(sched, &ctx,
+ &ctx.old_groups[prio],
+ false, true);
+ tick_ctx_pick_groups_from_list(sched, &ctx,
+ &sched->groups.idle[prio],
+ false, false);
+ }
}
tick_ctx_apply(sched, &ctx);
@@ -3064,6 +3369,8 @@ void panthor_sched_pre_reset(struct panthor_device *ptdev)
cancel_work_sync(&sched->sync_upd_work);
cancel_delayed_work_sync(&sched->tick_work);
+ handle_protm_fault(ptdev);
+
panthor_sched_suspend(ptdev);
/* Stop all groups that might still accept jobs, so we don't get passed
@@ -3089,6 +3396,11 @@ void panthor_sched_post_reset(struct panthor_device *ptdev, bool reset_failed)
mutex_lock(&sched->reset.lock);
+ if (sched->protm.active_group) {
+ group_put(sched->protm.active_group);
+ sched->protm.active_group = NULL;
+ }
+
list_for_each_entry_safe(group, group_tmp, &sched->reset.stopped_groups, run_node) {
/* Consider all previously running group as terminated if the
* reset failed.
diff --git a/drivers/gpu/drm/panthor/panthor_sched.h b/drivers/gpu/drm/panthor/panthor_sched.h
index be7e1c8b4f563..1082c4d977c5e 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.h
+++ b/drivers/gpu/drm/panthor/panthor_sched.h
@@ -52,4 +52,8 @@ void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events);
void panthor_fdinfo_gather_group_samples(struct drm_file *file);
+int panthor_sched_protm_block(struct panthor_device *ptdev);
+int panthor_sched_protm_try_block(struct panthor_device *ptdev);
+void panthor_sched_protm_unblock(struct panthor_device *ptdev);
+
#endif
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v3 6/6] drm/panthor: Expose protected rendering features
2026-09-11 11:40 [PATCH v3 0/6] drm/panthor: Protected mode support for Mali CSF GPUs Ketil Johnsen
` (4 preceding siblings ...)
2026-09-11 11:40 ` [PATCH v3 5/6] drm/panthor: Add support for entering and exiting protected mode Ketil Johnsen
@ 2026-09-11 11:40 ` Ketil Johnsen
2026-09-11 11:55 ` sashiko-bot
2026-09-11 14:42 ` Boris Brezillon
5 siblings, 2 replies; 11+ messages in thread
From: Ketil Johnsen @ 2026-09-11 11:40 UTC (permalink / raw)
To: Boris Brezillon, Steven Price, Liviu Dudau, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Daniel Almeida, Alice Ryhl
Cc: dri-devel, linux-kernel, Ketil Johnsen
From: Boris Brezillon <boris.brezillon@collabora.com>
Extensions to Panthor uAPI:
- New IOCTL for user space to provide protected FW memory.
- New query for checking protected rendering availability/status
and requirements.
- Extends group creation to allow user space to provide a protected
suspend buffer.
The Mali GPU FW needs some protected memory when executing in protected
mode. This FW memory section is assigned a VA during device init.
A user space process with the needed privileges (CAP_SYS_MODULE) must
provide a suitable memory buffer before the Mali GPU is capable of
executing in protected mode.
Processes who want to execute in protected mode must also ensure they
pass a protected suspend buffer during group creation.
Added panthor_kernel_bo_import() to allow user provided buffers.
Refactor panthor_kernel_bo_create() to allow shared code with the
new import variant.
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Co-developed-by: Ketil Johnsen <ketil.johnsen@arm.com>
Signed-off-by: Ketil Johnsen <ketil.johnsen@arm.com>
---
v3:
- Formating
- Rebase changed
- Keep local section_size var, needed for early out.
- Access fatal_queues and protm_pending_queues as atomics.
v2:
- First version of this change in this patch set.
---
drivers/gpu/drm/panthor/panthor_device.h | 3 +
drivers/gpu/drm/panthor/panthor_drv.c | 23 +++-
drivers/gpu/drm/panthor/panthor_fw.c | 159 +++++++++++++++++++----
drivers/gpu/drm/panthor/panthor_fw.h | 2 +
drivers/gpu/drm/panthor/panthor_gem.c | 106 ++++++++++-----
drivers/gpu/drm/panthor/panthor_gem.h | 7 +-
drivers/gpu/drm/panthor/panthor_sched.c | 45 ++++++-
include/uapi/drm/panthor_drm.h | 85 +++++++++++-
8 files changed, 356 insertions(+), 74 deletions(-)
diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h
index f1b7f51f5ae1e..241550964b04c 100644
--- a/drivers/gpu/drm/panthor/panthor_device.h
+++ b/drivers/gpu/drm/panthor/panthor_device.h
@@ -366,6 +366,9 @@ struct panthor_device {
/** @protm_exit_count: Number of times exited protm. */
atomic64_t protm_exit_count;
+
+ /** @info: Protected mode info. */
+ struct drm_panthor_protm_info info;
} protm;
};
diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c
index 51eb77633602f..875c5ef3e2748 100644
--- a/drivers/gpu/drm/panthor/panthor_drv.c
+++ b/drivers/gpu/drm/panthor/panthor_drv.c
@@ -178,11 +178,13 @@ panthor_get_uobj_array(const struct drm_panthor_obj_array *in, u32 min_stride,
PANTHOR_UOBJ_DECL(struct drm_panthor_mmu_info, page_size_bitmap), \
PANTHOR_UOBJ_DECL(struct drm_panthor_timestamp_info, current_timestamp), \
PANTHOR_UOBJ_DECL(struct drm_panthor_group_priorities_info, pad), \
+ PANTHOR_UOBJ_DECL(struct drm_panthor_protm_info, pad), \
PANTHOR_UOBJ_DECL(struct drm_panthor_sync_op, timeline_value), \
PANTHOR_UOBJ_DECL(struct drm_panthor_queue_submit, syncs), \
PANTHOR_UOBJ_DECL(struct drm_panthor_queue_create, ringbuf_size), \
PANTHOR_UOBJ_DECL(struct drm_panthor_vm_bind_op, syncs), \
- PANTHOR_UOBJ_DECL(struct drm_panthor_bo_sync_op, size))
+ PANTHOR_UOBJ_DECL(struct drm_panthor_bo_sync_op, size), \
+ PANTHOR_UOBJ_DECL(struct drm_panthor_protm_init, pad))
/**
* PANTHOR_UOBJ_SET() - Copy a kernel object to a user object.
@@ -959,6 +961,10 @@ static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct d
args->size = sizeof(ptdev->mmu_info);
return 0;
+ case DRM_PANTHOR_DEV_QUERY_PROTM_INFO:
+ args->size = sizeof(ptdev->protm.info);
+ return 0;
+
default:
return -EINVAL;
}
@@ -992,6 +998,9 @@ static int panthor_ioctl_dev_query(struct drm_device *ddev, void *data, struct d
case DRM_PANTHOR_DEV_QUERY_MMU_INFO:
return PANTHOR_UOBJ_SET(args->pointer, args->size, ptdev->mmu_info);
+ case DRM_PANTHOR_DEV_QUERY_PROTM_INFO:
+ return PANTHOR_UOBJ_SET(args->pointer, args->size, ptdev->protm.info);
+
default:
return -EINVAL;
}
@@ -1589,6 +1598,12 @@ static int panthor_ioctl_bo_query_info(struct drm_device *ddev, void *data,
return 0;
}
+static int panthor_ioctl_protm_init(struct drm_device *ddev, void *data,
+ struct drm_file *file)
+{
+ return panthor_fw_protm_init(file, data);
+}
+
static int
panthor_open(struct drm_device *ddev, struct drm_file *file)
{
@@ -1665,6 +1680,7 @@ static const struct drm_ioctl_desc panthor_drm_driver_ioctls[] = {
PANTHOR_IOCTL(SET_USER_MMIO_OFFSET, set_user_mmio_offset, DRM_RENDER_ALLOW),
PANTHOR_IOCTL(BO_SYNC, bo_sync, DRM_RENDER_ALLOW),
PANTHOR_IOCTL(BO_QUERY_INFO, bo_query_info, DRM_RENDER_ALLOW),
+ PANTHOR_IOCTL(PROTM_INIT, protm_init, DRM_RENDER_ALLOW),
};
static int panthor_mmap(struct file *filp, struct vm_area_struct *vma)
@@ -1785,6 +1801,9 @@ static void panthor_debugfs_init(struct drm_minor *minor)
* - 1.8 - extends DEV_QUERY_TIMESTAMP_INFO with flags
* - 1.9 - adds DRM_PANTHOR_DEV_QUERY_MMU_INFO query
* - adds DRM_PANTHOR_VM_BIND_OP_MAP_SPARSE flag
+ * - 1.10 - adds DRM_IOCTL_PANTHOR_PROTM_INIT ioctl
+ * - adds DRM_PANTHOR_DEV_QUERY_PROTM_INFO query
+ * - adds drm_panthor_group_create::protected_suspend_bo_handle
*/
static const struct drm_driver panthor_drm_driver = {
.driver_features = DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ |
@@ -1798,7 +1817,7 @@ static const struct drm_driver panthor_drm_driver = {
.name = "panthor",
.desc = "Panthor DRM driver",
.major = 1,
- .minor = 9,
+ .minor = 10,
.gem_prime_import_sg_table = panthor_gem_prime_import_sg_table,
.gem_prime_import = panthor_gem_prime_import,
diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
index 96770ce34da84..4b44baf82a037 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.c
+++ b/drivers/gpu/drm/panthor/panthor_fw.c
@@ -13,8 +13,10 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/capability.h>
#include <drm/drm_drv.h>
+#include <drm/drm_file.h>
#include <drm/drm_managed.h>
#include <drm/drm_print.h>
@@ -198,6 +200,12 @@ struct panthor_fw_section {
/** @size: Size of @buf in bytes. */
size_t size;
} data;
+
+ /** @size: Section size. */
+ size_t size;
+
+ /** @va: Section VA. */
+ u32 va;
};
#define CSF_MCU_SHARED_REGION_START 0x04000000ULL
@@ -246,6 +254,9 @@ struct panthor_fw {
/** @shared_section: The section containing the FW interfaces. */
struct panthor_fw_section *shared_section;
+ /** @protm_section: The protected mode section. */
+ struct panthor_fw_section *protm_section;
+
/** @iface: FW interfaces. */
struct panthor_fw_iface iface;
@@ -255,6 +266,9 @@ struct panthor_fw {
struct delayed_work ping_work;
} watchdog;
+ /** @protm_init_lock: Used to serialize protm initialization. */
+ struct mutex protm_init_lock;
+
/**
* @req_waitqueue: FW request waitqueue.
*
@@ -543,6 +557,31 @@ panthor_fw_alloc_suspend_buf_mem(struct panthor_device *ptdev, size_t size)
"FW suspend buffer");
}
+static u32 section_vm_map_flags(const struct panthor_fw_section *section)
+{
+ u32 cache_mode = section->flags &
+ CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_MASK;
+ u32 vm_map_flags = 0;
+
+ if (!(section->flags & CSF_FW_BINARY_IFACE_ENTRY_WR))
+ vm_map_flags |= DRM_PANTHOR_VM_BIND_OP_MAP_READONLY;
+
+ if (!(section->flags & CSF_FW_BINARY_IFACE_ENTRY_EX))
+ vm_map_flags |= DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC;
+
+ /* TODO: CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_*_COHERENT are mapped to
+ * non-cacheable for now. We might want to introduce a new
+ * IOMMU_xxx flag (or abuse IOMMU_MMIO, which maps to device
+ * memory and is currently not used by our driver) for
+ * AS_MEMATTR_AARCH64_SHARED memory, so we can take benefit
+ * of IO-coherent systems.
+ */
+ if (cache_mode != CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_CACHED)
+ vm_map_flags |= DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED;
+
+ return vm_map_flags;
+}
+
static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
const struct firmware *fw,
struct panthor_fw_binary_iter *iter,
@@ -590,12 +629,6 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
return -EINVAL;
}
- if (hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_PROT) {
- drm_warn(&ptdev->base,
- "Firmware protected mode entry is not supported, ignoring");
- return 0;
- }
-
if (hdr.va.start == CSF_MCU_SHARED_REGION_START &&
!(hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_SHARED)) {
drm_err(&ptdev->base,
@@ -644,35 +677,36 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
section->name = name;
}
- if (section_size) {
- u32 cache_mode = hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_MASK;
- u32 vm_map_flags = 0;
- u64 va = hdr.va.start;
+ section->size = section_size;
+ section->va = hdr.va.start;
- if (!(hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_WR))
- vm_map_flags |= DRM_PANTHOR_VM_BIND_OP_MAP_READONLY;
+ if (hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_PROT) {
+ if (ptdev->fw->protm_section) {
+ drm_err(&ptdev->base,
+ "Only one protected section supported\n");
+ return -EINVAL;
+ }
- if (!(hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_EX))
- vm_map_flags |= DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC;
+ ptdev->protm.info.fw_protected_sections_size =
+ ALIGN(section->size, vm_pgsz);
+ ptdev->fw->protm_section = section;
+ }
- /* TODO: CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_*_COHERENT are mapped to
- * non-cacheable for now. We might want to introduce a new
- * IOMMU_xxx flag (or abuse IOMMU_MMIO, which maps to device
- * memory and is currently not used by our driver) for
- * AS_MEMATTR_AARCH64_SHARED memory, so we can take benefit
- * of IO-coherent systems.
- */
- if (cache_mode != CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_CACHED)
- vm_map_flags |= DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED;
+ /* Defer the section->mem creation if this is a protected entry.
+ * This will be populated when DRM_IOCTL_PANTHOR_PROTM_INIT is called.
+ */
+ if (section->size && !(hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_PROT)) {
+ u32 vm_map_flags = section_vm_map_flags(section);
- section->mem = panthor_kernel_bo_create(ptdev, panthor_fw_vm(ptdev),
- section_size,
- DRM_PANTHOR_BO_NO_MMAP,
- vm_map_flags, va, "FW section");
+ section->mem = panthor_kernel_bo_create(
+ ptdev, panthor_fw_vm(ptdev), section->size,
+ DRM_PANTHOR_BO_NO_MMAP, vm_map_flags, section->va,
+ "FW section");
if (IS_ERR(section->mem))
return PTR_ERR(section->mem);
- if (drm_WARN_ON(&ptdev->base, section->mem->va_node.start != hdr.va.start))
+ if (drm_WARN_ON(&ptdev->base,
+ section->mem->va_node.start != section->va))
return -EINVAL;
if (section->flags & CSF_FW_BINARY_IFACE_ENTRY_SHARED) {
@@ -968,6 +1002,11 @@ static int panthor_init_csg_iface(struct panthor_device *ptdev,
return -EINVAL;
}
+ if (!csg_idx) {
+ ptdev->protm.info.group_protected_suspend_buf_size =
+ csg_iface->control->protm_suspend_size;
+ }
+
if (csg_idx > 0) {
struct panthor_fw_csg_iface *first_csg_iface =
panthor_fw_get_csg_iface(ptdev, 0);
@@ -1566,6 +1605,66 @@ int panthor_fw_protm_exit(struct panthor_device *ptdev, u32 timeout_ms)
return ret;
}
+int panthor_fw_protm_init(struct drm_file *file,
+ struct drm_panthor_protm_init *args)
+{
+ struct panthor_file *pfile = file->driver_priv;
+ struct panthor_device *ptdev = pfile->ptdev;
+ struct panthor_fw_section *protm_section = ptdev->fw->protm_section;
+ struct drm_gem_object *obj;
+ u32 vm_map_flags;
+ int cookie, ret = 0;
+
+ if (!capable(CAP_SYS_MODULE))
+ return -EPERM;
+
+ if (args->pad)
+ return -EINVAL;
+
+ if (!protm_section || !protm_section->size)
+ return -EINVAL;
+
+ guard(mutex)(&ptdev->fw->protm_init_lock);
+
+ if (ptdev->protm.info.state & DRM_PANTHOR_PROTM_INITIALIZED)
+ return 0;
+
+ if (!drm_dev_enter(&ptdev->base, &cookie))
+ return -ENODEV;
+
+ obj = drm_gem_object_lookup(file,
+ args->fw_protected_sections_bo_handle);
+ if (!obj) {
+ ret = -ENOENT;
+ goto out_dev_exit;
+ }
+
+ if (obj->size < ptdev->protm.info.fw_protected_sections_size) {
+ ret = -EINVAL;
+ goto out_gem_put;
+ }
+
+ vm_map_flags = section_vm_map_flags(protm_section);
+
+ protm_section->mem = panthor_kernel_bo_import(
+ ptdev, panthor_fw_vm(ptdev), to_panthor_bo(obj), vm_map_flags,
+ protm_section->va, protm_section->size);
+ if (IS_ERR(protm_section->mem)) {
+ ret = PTR_ERR(protm_section->mem);
+ protm_section->mem = NULL;
+ goto out_gem_put;
+ }
+
+ ptdev->protm.info.state |= DRM_PANTHOR_PROTM_INITIALIZED;
+
+out_gem_put:
+ drm_gem_object_put(obj);
+
+out_dev_exit:
+ drm_dev_exit(cookie);
+ return ret;
+}
+
/**
* panthor_fw_init() - Initialize FW related data.
* @ptdev: Device.
@@ -1587,6 +1686,10 @@ int panthor_fw_init(struct panthor_device *ptdev)
INIT_LIST_HEAD(&fw->sections);
INIT_DELAYED_WORK(&fw->watchdog.ping_work, panthor_fw_ping_work);
+ ret = drmm_mutex_init(&ptdev->base, &fw->protm_init_lock);
+ if (ret)
+ return ret;
+
irq = platform_get_irq_byname(to_platform_device(ptdev->base.dev), "job");
if (irq <= 0)
return -ENODEV;
diff --git a/drivers/gpu/drm/panthor/panthor_fw.h b/drivers/gpu/drm/panthor/panthor_fw.h
index 4eda8f8e714c1..34bc9dc82314c 100644
--- a/drivers/gpu/drm/panthor/panthor_fw.h
+++ b/drivers/gpu/drm/panthor/panthor_fw.h
@@ -529,6 +529,8 @@ static inline int panthor_fw_resume(struct panthor_device *ptdev)
int panthor_fw_init(struct panthor_device *ptdev);
void panthor_fw_unplug(struct panthor_device *ptdev);
+int panthor_fw_protm_init(struct drm_file *file,
+ struct drm_panthor_protm_init *args);
int panthor_fw_protm_enter(struct panthor_device *ptdev);
int panthor_fw_protm_exit(struct panthor_device *ptdev, u32 timeout_ms);
int panthor_fw_protm_exit_wait(struct panthor_device *ptdev, u32 timeout_ms);
diff --git a/drivers/gpu/drm/panthor/panthor_gem.c b/drivers/gpu/drm/panthor/panthor_gem.c
index c07a44057e426..9f9f68582ccf1 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.c
+++ b/drivers/gpu/drm/panthor/panthor_gem.c
@@ -74,14 +74,16 @@ static void panthor_gem_debugfs_bo_rm(struct panthor_gem_object *bo)
mutex_unlock(&ptdev->gems.lock);
}
-static void panthor_gem_debugfs_set_usage_flags(struct panthor_gem_object *bo, u32 usage_flags)
+static void panthor_gem_debugfs_add_usage_flags(struct panthor_gem_object *bo,
+ u32 usage_flags)
{
- bo->debugfs.flags = usage_flags;
- panthor_gem_debugfs_bo_add(bo);
+ atomic_or(usage_flags, &bo->debugfs.flags);
}
#else
+static void panthor_gem_debugfs_bo_add(struct panthor_gem_object *bo) {}
static void panthor_gem_debugfs_bo_rm(struct panthor_gem_object *bo) {}
-static void panthor_gem_debugfs_set_usage_flags(struct panthor_gem_object *bo, u32 usage_flags) {}
+static void panthor_gem_debugfs_add_usage_flags(struct panthor_gem_object *bo,
+ u32 usage_flags) {}
static void panthor_gem_debugfs_bo_init(struct panthor_gem_object *bo) {}
#endif
@@ -1031,7 +1033,7 @@ panthor_gem_create(struct drm_device *dev, size_t size, uint32_t flags,
bo->base.resv = bo->exclusive_vm_root_gem->resv;
}
- panthor_gem_debugfs_set_usage_flags(bo, usage_flags);
+ panthor_gem_debugfs_bo_add(bo);
return bo;
err_put:
@@ -1257,7 +1259,9 @@ void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo)
panthor_kernel_bo_vunmap(bo);
drm_WARN_ON(bo->obj->dev,
- to_panthor_bo(bo->obj)->exclusive_vm_root_gem != panthor_vm_root_gem(vm));
+ to_panthor_bo(bo->obj)->exclusive_vm_root_gem &&
+ (to_panthor_bo(bo->obj)->exclusive_vm_root_gem !=
+ panthor_vm_root_gem(vm)));
panthor_vm_unmap_range(vm, bo->va_node.start, bo->va_node.size);
panthor_vm_free_va(vm, &bo->va_node);
if (vm == panthor_fw_vm(ptdev))
@@ -1268,46 +1272,32 @@ void panthor_kernel_bo_destroy(struct panthor_kernel_bo *bo)
}
/**
- * panthor_kernel_bo_create() - Create and map a GEM object to a VM
+ * panthor_kernel_bo_import() - Create a kernel BO from an existing GEM object
* @ptdev: Device.
* @vm: VM to map the GEM to.
- * @size: Size of the buffer object.
- * @bo_flags: Combination of drm_panthor_bo_flags flags.
+ * @bo: BO to use for our kernel BO.
* @vm_map_flags: Combination of drm_panthor_vm_bind_op_flags (only those
* that are related to map operations).
* @gpu_va: GPU address assigned when mapping to the VM.
* If gpu_va == PANTHOR_VM_KERNEL_AUTO_VA, the virtual address will be
* automatically allocated.
- * @name: Descriptive label of the BO's contents
+ * @vm_map_size: Size of the BO to map to the VM.
*
* Return: A valid pointer in case of success, an ERR_PTR() otherwise.
*/
struct panthor_kernel_bo *
-panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
- size_t size, u32 bo_flags, u32 vm_map_flags,
- u64 gpu_va, const char *name)
+panthor_kernel_bo_import(struct panthor_device *ptdev, struct panthor_vm *vm,
+ struct panthor_gem_object *bo, u32 vm_map_flags,
+ u64 gpu_va, u32 vm_map_size)
{
struct panthor_kernel_bo *kbo;
- struct panthor_gem_object *bo;
- u32 debug_flags = PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL;
int ret;
- if (drm_WARN_ON(&ptdev->base, !vm))
- return ERR_PTR(-EINVAL);
-
kbo = kzalloc_obj(*kbo);
if (!kbo)
return ERR_PTR(-ENOMEM);
- if (vm == panthor_fw_vm(ptdev))
- debug_flags |= PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED;
-
- bo = panthor_gem_create(&ptdev->base, size, bo_flags, vm, debug_flags);
- if (IS_ERR(bo)) {
- ret = PTR_ERR(bo);
- goto err_free_kbo;
- }
-
+ drm_gem_object_get(&bo->base);
kbo->obj = &bo->base;
if (vm == panthor_fw_vm(ptdev)) {
@@ -1316,24 +1306,27 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
goto err_put_obj;
}
- panthor_gem_kernel_bo_set_label(kbo, name);
-
/* The system and GPU MMU page size might differ, which becomes a
* problem for FW sections that need to be mapped at explicit address
* since our PAGE_SIZE alignment might cover a VA range that's
* expected to be used for another section.
* Make sure we never map more than we need.
*/
- size = ALIGN(size, panthor_vm_page_size(vm));
- ret = panthor_vm_alloc_va(vm, gpu_va, size, &kbo->va_node);
+ vm_map_size = ALIGN(vm_map_size, panthor_vm_page_size(vm));
+ ret = panthor_vm_alloc_va(vm, gpu_va, vm_map_size, &kbo->va_node);
if (ret)
goto err_unpin;
- ret = panthor_vm_map_bo_range(vm, bo, 0, size, kbo->va_node.start, vm_map_flags);
+ ret = panthor_vm_map_bo_range(vm, bo, 0, vm_map_size,
+ kbo->va_node.start, vm_map_flags);
if (ret)
goto err_free_va;
kbo->vm = panthor_vm_get(vm);
+ if (vm == panthor_fw_vm(ptdev))
+ panthor_gem_debugfs_add_usage_flags(
+ bo, PANTHOR_DEBUGFS_GEM_USAGE_FLAG_FW_MAPPED);
+
return kbo;
err_free_va:
@@ -1345,12 +1338,55 @@ panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
err_put_obj:
drm_gem_object_put(&bo->base);
-
-err_free_kbo:
kfree(kbo);
return ERR_PTR(ret);
}
+/**
+ * panthor_kernel_bo_create() - Create and map a GEM object to a VM
+ * @ptdev: Device.
+ * @vm: VM to map the GEM to.
+ * @size: Size of the buffer object.
+ * @bo_flags: Combination of drm_panthor_bo_flags flags.
+ * @vm_map_flags: Combination of drm_panthor_vm_bind_op_flags (only those
+ * that are related to map operations).
+ * @gpu_va: GPU address assigned when mapping to the VM.
+ * If gpu_va == PANTHOR_VM_KERNEL_AUTO_VA, the virtual address will be
+ * automatically allocated.
+ * @name: Descriptive label of the BO's contents
+ *
+ * Return: A valid pointer in case of success, an ERR_PTR() otherwise.
+ */
+struct panthor_kernel_bo *
+panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
+ size_t size, u32 bo_flags, u32 vm_map_flags,
+ u64 gpu_va, const char *name)
+{
+ struct panthor_kernel_bo *kbo;
+ struct panthor_gem_object *bo;
+
+ if (drm_WARN_ON(&ptdev->base, !vm))
+ return ERR_PTR(-EINVAL);
+
+ bo = panthor_gem_create(&ptdev->base, size, bo_flags, vm, 0);
+ if (IS_ERR(bo))
+ return ERR_CAST(bo);
+
+ kbo = panthor_kernel_bo_import(ptdev, vm, bo, vm_map_flags, gpu_va,
+ size);
+ if (!IS_ERR(kbo)) {
+ panthor_gem_debugfs_add_usage_flags(
+ bo, PANTHOR_DEBUGFS_GEM_USAGE_FLAG_KERNEL);
+ panthor_gem_kernel_bo_set_label(kbo, name);
+ }
+
+ /* panthor_kernel_bo_import() acquires a GEM ref if the import succeeds, so
+ * we can release it unconditionally here.
+ */
+ drm_gem_object_put(&bo->base);
+ return kbo;
+}
+
/**
* panthor_dummy_bo_create() - Create a Panthor BO meant to back sparse bindings.
* @ptdev: Device.
@@ -1644,9 +1680,9 @@ static void panthor_gem_debugfs_bo_print(struct panthor_gem_object *bo,
enum panthor_gem_reclaim_state reclaim_state = bo->reclaim_state;
unsigned int refcount = kref_read(&bo->base.refcount);
int reclaimed_count = atomic_read(&bo->reclaimed_count);
+ u32 gem_usage_flags = atomic_read(&bo->debugfs.flags);
char creator_info[32] = {};
size_t resident_size;
- u32 gem_usage_flags = bo->debugfs.flags;
u32 gem_state_flags = 0;
/* Skip BOs being destroyed. */
diff --git a/drivers/gpu/drm/panthor/panthor_gem.h b/drivers/gpu/drm/panthor/panthor_gem.h
index 5ae37d0d3646f..7e88f5dcbff54 100644
--- a/drivers/gpu/drm/panthor/panthor_gem.h
+++ b/drivers/gpu/drm/panthor/panthor_gem.h
@@ -62,7 +62,7 @@ struct panthor_gem_debugfs {
} creator;
/** @flags: Combination of panthor_debugfs_gem_usage_flags flags */
- u32 flags;
+ atomic_t flags;
};
/**
@@ -318,6 +318,11 @@ panthor_kernel_bo_vunmap(struct panthor_kernel_bo *bo)
}
}
+struct panthor_kernel_bo *
+panthor_kernel_bo_import(struct panthor_device *ptdev, struct panthor_vm *vm,
+ struct panthor_gem_object *bo,
+ u32 vm_map_flags, u64 gpu_va, u32 vm_map_size);
+
struct panthor_kernel_bo *
panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
size_t size, u32 bo_flags, u32 vm_map_flags,
diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
index 1fe77e5c41995..d73fc2f6633a6 100644
--- a/drivers/gpu/drm/panthor/panthor_sched.c
+++ b/drivers/gpu/drm/panthor/panthor_sched.c
@@ -1429,10 +1429,15 @@ cs_slot_process_protm_pending_event_locked(struct panthor_device *ptdev,
if (!group)
return;
- /* Do not allow user space work to switch into protected mode, as we
- * do not fully support this quite yet.
+ /* Do not allow user space work to switch into protected mode if we
+ * do not support protected mode on this device.
+ * User space should query (and init) this support before attempting
+ * to use such GPU instructions.
*/
- atomic_or(BIT(cs_id), &group->fatal_queues);
+ if (!(ptdev->protm.info.state & DRM_PANTHOR_PROTM_INITIALIZED))
+ atomic_or(BIT(cs_id), &group->fatal_queues);
+ else
+ atomic_or(BIT(cs_id), &group->protm_pending_queues);
sched_queue_delayed_work(sched, tick, 0);
}
@@ -3963,6 +3968,7 @@ static void add_group_kbo_sizes(struct panthor_device *ptdev,
}
#define MAX_GROUPS_PER_POOL 128
+#define GROUP_CREATE_FLAGS DRM_PANTHOR_GROUP_CREATE_PROTECTED
int panthor_group_create(struct drm_file *file,
const struct drm_panthor_group_create *group_args,
@@ -3977,9 +3983,6 @@ int panthor_group_create(struct drm_file *file,
u32 gid, i, suspend_size;
int ret;
- if (group_args->pad)
- return -EINVAL;
-
if (group_args->priority >= PANTHOR_CSG_PRIORITY_COUNT)
return -EINVAL;
@@ -4030,6 +4033,36 @@ int panthor_group_create(struct drm_file *file,
goto err_put_group;
}
+ if (group_args->protected_suspend_bo_handle) {
+ struct drm_gem_object *obj;
+
+ obj = drm_gem_object_lookup(file, group_args->protected_suspend_bo_handle);
+ if (!obj) {
+ ret = -ENOENT;
+ goto err_put_group;
+ }
+
+ if (obj->size < ptdev->protm.info.group_protected_suspend_buf_size) {
+ drm_gem_object_put(obj);
+ ret = -EINVAL;
+ goto err_put_group;
+ }
+
+ suspend_size = csg_iface->control->protm_suspend_size;
+ group->protm_suspend_buf =
+ panthor_kernel_bo_import(ptdev, panthor_fw_vm(ptdev),
+ to_panthor_bo(obj),
+ DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC,
+ PANTHOR_VM_KERNEL_AUTO_VA,
+ suspend_size);
+ drm_gem_object_put(obj);
+ if (IS_ERR(group->protm_suspend_buf)) {
+ ret = PTR_ERR(group->protm_suspend_buf);
+ group->protm_suspend_buf = NULL;
+ goto err_put_group;
+ }
+ }
+
group->syncobjs = panthor_kernel_bo_create(ptdev, group->vm,
group_args->queues.count *
sizeof(struct panthor_syncobj_64b),
diff --git a/include/uapi/drm/panthor_drm.h b/include/uapi/drm/panthor_drm.h
index a2ff0f4ec6915..a0ed16041e178 100644
--- a/include/uapi/drm/panthor_drm.h
+++ b/include/uapi/drm/panthor_drm.h
@@ -154,6 +154,11 @@ enum drm_panthor_ioctl_id {
* This is useful for imported BOs.
*/
DRM_PANTHOR_BO_QUERY_INFO,
+
+ /**
+ * @DRM_PANTHOR_PROTM_INIT: Device-wide initialize of protected mode.
+ */
+ DRM_PANTHOR_PROTM_INIT,
};
/**
@@ -256,6 +261,11 @@ enum drm_panthor_dev_query_type {
/** @DRM_PANTHOR_DEV_QUERY_MMU_INFO: Query MMU information. */
DRM_PANTHOR_DEV_QUERY_MMU_INFO,
+
+ /**
+ * @DRM_PANTHOR_DEV_QUERY_PROTM_INFO: Query supported protected rendering information.
+ */
+ DRM_PANTHOR_DEV_QUERY_PROTM_INFO,
};
/**
@@ -517,6 +527,51 @@ struct drm_panthor_group_priorities_info {
__u8 pad[3];
};
+/**
+ * enum drm_panthor_protm_state_flags - Describes the state of the protected mode feature.
+ *
+ * List of GPU states which can be used by the GPU to access protected memory.
+ */
+enum drm_panthor_protm_state_flags {
+ /**
+ * @DRM_PANTHOR_PROTM_INITIALIZED: Device-wide initialization of the
+ * protected mode feature is done.
+ */
+ DRM_PANTHOR_PROTM_INITIALIZED = 1 << 0,
+};
+
+/**
+ * struct drm_panthor_protm_info - Protected mode info.
+ *
+ * Structure grouping all queryable information relating to protected mode.
+ */
+struct drm_panthor_protm_info {
+ /**
+ * @state: Combination of enum drm_panthor_protm_state_flags flags.
+ */
+ __u32 state;
+
+ /**
+ * @fw_protected_sections_size: Size of all the protected FW sections.
+ *
+ * Size of the protected buffer to pass through
+ * DRM_IOCTL_PANTHOR_PROTM_INIT.
+ */
+ __u32 fw_protected_sections_size;
+
+ /**
+ * @group_protected_suspend_buf_size: Size of the group suspend buffer.
+ *
+ * This must be used to allocate a protected BO that's big enough to use
+ * as a protected suspend buffer when a group supports protected
+ * rendering.
+ */
+ __u32 group_protected_suspend_buf_size;
+
+ /** @pad: MBZ. */
+ __u32 pad;
+};
+
/**
* struct drm_panthor_dev_query - Arguments passed to DRM_PANTHOR_IOCTL_DEV_QUERY
*/
@@ -901,8 +956,14 @@ struct drm_panthor_group_create {
/** @priority: Group priority (see enum drm_panthor_group_priority). */
__u8 priority;
- /** @pad: Padding field, MBZ. */
- __u32 pad;
+ /**
+ * @protected_suspend_bo_handle: BO to use as a protected suspend buffer.
+ *
+ * This BO must have been allocated from a protected DMA-BUF heap and
+ * imported in panthor. It's size must be at least
+ * drm_panthor_protm_info::group_protected_suspend_buf_size.
+ */
+ __u32 protected_suspend_bo_handle;
/**
* @compute_core_mask: Mask encoding cores that can be used for compute jobs.
@@ -1270,6 +1331,24 @@ struct drm_panthor_bo_query_info {
__u32 pad;
};
+/**
+ * struct drm_panthor_protm_init - Protected mode initialization arguments.
+ */
+struct drm_panthor_protm_init {
+ /**
+ * @fw_protected_sections_bo_handle: Handle of the BO to use for the FW protected
+ * sections.
+ *
+ * This BO must have been allocated from a protected DMA-BUF heap and
+ * imported in panthor. It's size must be at least
+ * drm_panthor_protm_info::fw_protected_sections_size.
+ */
+ __u32 fw_protected_sections_bo_handle;
+
+ /** @pad: MBZ. */
+ __u32 pad;
+};
+
/**
* DRM_IOCTL_PANTHOR() - Build a Panthor IOCTL number
* @__access: Access type. Must be R, W or RW.
@@ -1320,6 +1399,8 @@ enum {
DRM_IOCTL_PANTHOR(WR, BO_SYNC, bo_sync),
DRM_IOCTL_PANTHOR_BO_QUERY_INFO =
DRM_IOCTL_PANTHOR(WR, BO_QUERY_INFO, bo_query_info),
+ DRM_IOCTL_PANTHOR_PROTM_INIT =
+ DRM_IOCTL_PANTHOR(WR, PROTM_INIT, protm_init),
};
#if defined(__cplusplus)
--
2.43.0
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v3 6/6] drm/panthor: Expose protected rendering features
2026-09-11 11:40 ` [PATCH v3 6/6] drm/panthor: Expose protected rendering features Ketil Johnsen
@ 2026-09-11 11:55 ` sashiko-bot
2026-09-11 14:42 ` Boris Brezillon
1 sibling, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-09-11 11:55 UTC (permalink / raw)
To: Ketil Johnsen; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] An unprivileged userspace process can trigger a full GPU reset by omitting the protected suspend buffer during group creation and then submitting commands that switch to protected mode.
- [High] Slow reset path triggers a NULL pointer dereference on the uninitialized protected firmware section in `panthor_fw_init_section_mem`.
- [Low] Data race on the `ptdev->protm.info.state` flag.
- [Low] Mixing `guard()` and `goto`-based cleanup in `panthor_fw_protm_init` violates subsystem cleanup guidelines.
--
commit b7ec40f4bb5c3ae17349c8d0c7a683e90abc16c4
Author: Boris Brezillon <boris.brezillon@collabora.com>
Subject: drm/panthor: Expose protected rendering features
This commit introduces extensions to the Panthor uAPI to support protected
rendering. It adds a new IOCTL for providing protected firmware memory and
extends group creation to allow userspace to provide a protected suspend
buffer.
> diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
> index 96770ce34da84..4b44baf82a037 100644
> --- a/drivers/gpu/drm/panthor/panthor_fw.c
> +++ b/drivers/gpu/drm/panthor/panthor_fw.c
[ ... ]
> @@ -644,35 +677,36 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev,
> section->name = name;
> }
>
> - if (section_size) {
> - u32 cache_mode = hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_MASK;
> - u32 vm_map_flags = 0;
> - u64 va = hdr.va.start;
> + section->size = section_size;
> + section->va = hdr.va.start;
>
> - if (!(hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_WR))
> - vm_map_flags |= DRM_PANTHOR_VM_BIND_OP_MAP_READONLY;
> + if (hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_PROT) {
> + if (ptdev->fw->protm_section) {
> + drm_err(&ptdev->base,
> + "Only one protected section supported\n");
> + return -EINVAL;
> + }
>
> - if (!(hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_EX))
> - vm_map_flags |= DRM_PANTHOR_VM_BIND_OP_MAP_NOEXEC;
> + ptdev->protm.info.fw_protected_sections_size =
> + ALIGN(section->size, vm_pgsz);
> + ptdev->fw->protm_section = section;
> + }
>
> - /* TODO: CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_*_COHERENT are mapped to
> - * non-cacheable for now. We might want to introduce a new
> - * IOMMU_xxx flag (or abuse IOMMU_MMIO, which maps to device
> - * memory and is currently not used by our driver) for
> - * AS_MEMATTR_AARCH64_SHARED memory, so we can take benefit
> - * of IO-coherent systems.
> - */
> - if (cache_mode != CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_CACHED)
> - vm_map_flags |= DRM_PANTHOR_VM_BIND_OP_MAP_UNCACHED;
> + /* Defer the section->mem creation if this is a protected entry.
> + * This will be populated when DRM_IOCTL_PANTHOR_PROTM_INIT is called.
> + */
> + if (section->size && !(hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_PROT)) {
> + u32 vm_map_flags = section_vm_map_flags(section);
[Severity: High]
Can this cause a kernel panic if a GPU hang occurs before the protected mode
is initialized?
Because section->mem is intentionally left as NULL here for protected
sections, if an unprivileged user triggers a GPU hang before calling the
PROTM_INIT ioctl, the slow reset path will be invoked. When
panthor_reload_fw_sections() iterates over all sections, it calls
panthor_fw_init_section_mem() which dereferences section->mem without checking
if it is valid (e.g., bool was_mapped = !!section->mem->kmap).
Would it make sense to skip uninitialized protected sections during reset?
[ ... ]
> @@ -1566,6 +1605,66 @@ int panthor_fw_protm_exit(struct panthor_device *ptdev, u32 timeout_ms)
> return ret;
> }
>
> +int panthor_fw_protm_init(struct drm_file *file,
> + struct drm_panthor_protm_init *args)
> +{
> + struct panthor_file *pfile = file->driver_priv;
> + struct panthor_device *ptdev = pfile->ptdev;
> + struct panthor_fw_section *protm_section = ptdev->fw->protm_section;
> + struct drm_gem_object *obj;
> + u32 vm_map_flags;
> + int cookie, ret = 0;
> +
> + if (!capable(CAP_SYS_MODULE))
> + return -EPERM;
> +
> + if (args->pad)
> + return -EINVAL;
> +
> + if (!protm_section || !protm_section->size)
> + return -EINVAL;
> +
> + guard(mutex)(&ptdev->fw->protm_init_lock);
[Severity: Low]
Is it safe to mix guard() and goto-based cleanup in the same function?
The cleanup subsystem guidelines suggest avoiding the mixture of scope-based
locks like guard(mutex) with goto labels (such as out_gem_put and
out_dev_exit) in the same routine, as it can lead to confusing ownership
semantics and future maintenance bugs.
> +
> + if (ptdev->protm.info.state & DRM_PANTHOR_PROTM_INITIALIZED)
> + return 0;
> +
> + if (!drm_dev_enter(&ptdev->base, &cookie))
> + return -ENODEV;
> +
> + obj = drm_gem_object_lookup(file,
> + args->fw_protected_sections_bo_handle);
> + if (!obj) {
> + ret = -ENOENT;
> + goto out_dev_exit;
> + }
> +
> + if (obj->size < ptdev->protm.info.fw_protected_sections_size) {
> + ret = -EINVAL;
> + goto out_gem_put;
> + }
> +
> + vm_map_flags = section_vm_map_flags(protm_section);
> +
> + protm_section->mem = panthor_kernel_bo_import(
> + ptdev, panthor_fw_vm(ptdev), to_panthor_bo(obj), vm_map_flags,
> + protm_section->va, protm_section->size);
> + if (IS_ERR(protm_section->mem)) {
> + ret = PTR_ERR(protm_section->mem);
> + protm_section->mem = NULL;
> + goto out_gem_put;
> + }
> +
> + ptdev->protm.info.state |= DRM_PANTHOR_PROTM_INITIALIZED;
[Severity: Low]
Could this non-atomic modification cause torn reads or compiler reordering
issues?
This updates ptdev->protm.info.state using a bitwise OR, but the same state
flag is read concurrently in cs_slot_process_protm_pending_event_locked()
without locks or READ_ONCE().
> +
> +out_gem_put:
> + drm_gem_object_put(obj);
> +
> +out_dev_exit:
> + drm_dev_exit(cookie);
> + return ret;
> +}
[ ... ]
> diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
> index 1fe77e5c41995..d73fc2f6633a6 100644
> --- a/drivers/gpu/drm/panthor/panthor_sched.c
> +++ b/drivers/gpu/drm/panthor/panthor_sched.c
[ ... ]
> @@ -1429,10 +1429,15 @@ cs_slot_process_protm_pending_event_locked(struct panthor_device *ptdev,
> if (!group)
> return;
>
> - /* Do not allow user space work to switch into protected mode, as we
> - * do not fully support this quite yet.
> + /* Do not allow user space work to switch into protected mode if we
> + * do not support protected mode on this device.
> + * User space should query (and init) this support before attempting
> + * to use such GPU instructions.
> */
> - atomic_or(BIT(cs_id), &group->fatal_queues);
> + if (!(ptdev->protm.info.state & DRM_PANTHOR_PROTM_INITIALIZED))
> + atomic_or(BIT(cs_id), &group->fatal_queues);
> + else
> + atomic_or(BIT(cs_id), &group->protm_pending_queues);
>
> sched_queue_delayed_work(sched, tick, 0);
> }
[Severity: High]
Does this allow an unprivileged userspace process to trigger a full
system-wide GPU reset?
The logic here approves a transition to protected mode by only checking the
global DRM_PANTHOR_PROTM_INITIALIZED flag. However, if a user creates a
group with protected_suspend_bo_handle set to 0 (resulting in
group->protm_suspend_buf being NULL), they can still submit a PROT_REGION
command.
Because the scheduler does not verify if the specific group provided a valid
suspend buffer, the hardware interface gets programmed with a zero address
for the suspend buffer in panthor_group_bind_locked(). When the firmware
later attempts to suspend the group, it writes to address 0, causing an
unrecoverable exception and Denial of Service.
Should this also check if group->protm_suspend_buf is valid?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260911114014.79139-1-ketil.johnsen@arm.com?part=6
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 5/6] drm/panthor: Add support for entering and exiting protected mode
2026-09-11 11:40 ` [PATCH v3 5/6] drm/panthor: Add support for entering and exiting protected mode Ketil Johnsen
@ 2026-09-11 11:56 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-09-11 11:56 UTC (permalink / raw)
To: Ketil Johnsen; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Polling for protected mode entry using `wait_event_timeout` without a wakeup source will unconditionally sleep for the full 500ms timeout.
- [High] Acknowledging a pending protected mode event during a slot reset does not clear the corresponding software state, causing a permanent malfunction for the group.
- [High] TOCTOU race in `panthor_gpu_protm_fault_pending` leads to missed faults and an infinite reset loop.
--
commit 134f4af77e10adece5a3a20d7839a25e0c7a720d
Author: Florent Tomasin <florent.tomasin@arm.com>
drm/panthor: Add support for entering and exiting protected mode
This commit modifies the Panthor driver to handle GPU hardware protected mode
enter and exit operations. It introduces the mechanisms needed for entering
and exiting protected mode, handling protected mode IRQs, scheduling
modifications, and GPU fault handling during protected execution.
> diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c
> index 5f9f7a92c56a8..96770ce34da84 100644
> --- a/drivers/gpu/drm/panthor/panthor_fw.c
> +++ b/drivers/gpu/drm/panthor/panthor_fw.c
[ ... ]
> @@ -1476,6 +1481,91 @@ static void panthor_fw_ping_work(struct work_struct *work)
> }
> }
>
> +static bool wait_protm_enter(struct panthor_device *ptdev,
> + long long enter_count)
> +{
> + return (panthor_gpu_status(ptdev) & GPU_STATUS_PROTM_ACTIVE) ||
> + (atomic64_read(&ptdev->protm.protm_exit_count) >= enter_count);
> +}
> +
> +int panthor_fw_protm_enter(struct panthor_device *ptdev)
> +{
[ ... ]
> + /* Poll for the entry of protected mode.
> + * It is possible that GPU_STATUS_PROTM_ACTIVE is set and cleared
> + * before we check it below, so we must also check for GLB_PROTM_EXIT.
> + * GLB_PROTM_EXIT can not be checked directly, because this could also
> + * be handled and clear before we check below. We count number of
> + * protm enters and exits to safely handle that case.
> + */
> + ret = wait_event_timeout(ptdev->fw->req_waitqueue,
> + wait_protm_enter(ptdev, enter_count),
> + msecs_to_jiffies(500));
[Severity: High]
Since GPU_STATUS_PROTM_ACTIVE is a hardware status bit and does not trigger a
new interrupt when it becomes true, wouldn't this wait queue never be woken
up? If the bit isn't set immediately on the first check in wait_protm_enter(),
it seems like this will unconditionally sleep for the full 500ms duration
before returning success, causing a half-second delay every time the GPU
enters protected mode.
> + if (!ret) {
> + drm_err(&ptdev->base,
> + "Wait for GPU protected mode enter timed out");
> + return -ETIMEDOUT;
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c
> index a383b04f101ed..27617f3a72394 100644
> --- a/drivers/gpu/drm/panthor/panthor_gpu.c
> +++ b/drivers/gpu/drm/panthor/panthor_gpu.c
[ ... ]
> @@ -123,6 +152,13 @@ static irqreturn_t panthor_gpu_irq_threaded_handler(int irq, void *data)
> return panthor_irq_default_threaded_handler(data, panthor_gpu_irq_handler);
> }
>
> +bool panthor_gpu_protm_fault_pending(struct panthor_device *ptdev)
> +{
> + return atomic_read(&ptdev->gpu->protm_fault) ||
> + gpu_read(ptdev->gpu->irq.iomem, INT_RAWSTAT) &
> + GPU_IRQ_PROTM_FAULT;
> +}
[Severity: High]
Could there be a race condition here due to the short-circuit evaluation?
If CPU 0 evaluates the left side and reads the software flag
ptdev->gpu->protm_fault as 0, and is then preempted, CPU 1 could execute the
interrupt handler, set the software flag to 1, and clear the hardware
register.
When CPU 0 resumes to evaluate the right side, it would read the hardware
register as 0 and return false. This would cause the fault to be missed
entirely (e.g., in sched_process_protm_exit_event_locked()), potentially
allowing a faulty protected mode group to restart and trigger an infinite
reset loop.
> diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
> index 0c8ea07fc7b9d..1fe77e5c41995 100644
> --- a/drivers/gpu/drm/panthor/panthor_sched.c
> +++ b/drivers/gpu/drm/panthor/panthor_sched.c
[ ... ]
> @@ -1149,12 +1170,14 @@ cs_slot_reset_locked(struct panthor_device *ptdev, u32 csg_id, u32 cs_id)
> struct panthor_fw_cs_iface *cs_iface = panthor_fw_get_cs_iface(ptdev, csg_id, cs_id);
> struct panthor_group *group = ptdev->scheduler->csg_slots[csg_id].group;
> struct panthor_queue *queue = group->queues[cs_id];
> + u32 val, mask;
>
> lockdep_assert_held(&ptdev->scheduler->lock);
>
> - panthor_fw_update_reqs(cs_iface, req,
> - CS_STATE_STOP,
> - CS_STATE_MASK);
> + val = CS_STATE_STOP | (cs_iface->output->ack & CS_PROTM_PENDING);
> + mask = CS_STATE_MASK | CS_PROTM_PENDING;
> +
> + panthor_fw_update_reqs(cs_iface, req, val, mask);
[Severity: High]
When acknowledging the pending CS_PROTM_PENDING hardware event here during a
slot reset, the software state group->protm_pending_queues is not cleared.
Later, tick_ctx_handle_protm_group() only clears bits from
group->protm_pending_queues if the hardware event is still pending via
(ack ^ req) & CS_PROTM_PENDING. Since the hardware event was already cleared
here, will the software bit remain permanently set, causing the group to
perpetually request entry into protected mode?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260911114014.79139-1-ketil.johnsen@arm.com?part=5
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 2/6] drm/panthor: Minor scheduler refactoring
2026-09-11 11:40 ` [PATCH v3 2/6] drm/panthor: Minor scheduler refactoring Ketil Johnsen
@ 2026-09-11 14:23 ` Boris Brezillon
0 siblings, 0 replies; 11+ messages in thread
From: Boris Brezillon @ 2026-09-11 14:23 UTC (permalink / raw)
To: Ketil Johnsen
Cc: Steven Price, Liviu Dudau, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Daniel Almeida,
Alice Ryhl, dri-devel, linux-kernel, Florent Tomasin
On Fri, 11 Sep 2026 13:40:10 +0200
Ketil Johnsen <ketil.johnsen@arm.com> wrote:
> From: Florent Tomasin <florent.tomasin@arm.com>
>
> Refactor parts of the group scheduling logic into new helper functions.
> This will simplify addition of the protected mode feature.
>
> Remove redundant assignments of csg_slot.
>
> Signed-off-by: Florent Tomasin <florent.tomasin@arm.com>
> Co-developed-by: Ketil Johnsen <ketil.johnsen@arm.com>
> Signed-off-by: Ketil Johnsen <ketil.johnsen@arm.com>
Reviewed-by: Boris Brezillon <boris.brezillon@collabora.com>
>
> ---
>
> v3:
> - Use panthor_fw_csg_endpoint_req_update() (was rebase mistake)
> - Function rename to tick_ctx_update_group_prio()
>
> v2:
> - Moved option to ding only the CSG doorbell to later patch
> ---
> drivers/gpu/drm/panthor/panthor_sched.c | 131 ++++++++++++++----------
> 1 file changed, 79 insertions(+), 52 deletions(-)
>
> diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
> index 60b2417deb81b..1123cf36a7bca 100644
> --- a/drivers/gpu/drm/panthor/panthor_sched.c
> +++ b/drivers/gpu/drm/panthor/panthor_sched.c
> @@ -2344,12 +2344,81 @@ tick_ctx_cleanup(struct panthor_scheduler *sched,
> }
> }
>
> +static void
> +tick_ctx_evict_group(struct panthor_scheduler *sched,
> + struct panthor_csg_slots_upd_ctx *upd_ctx,
> + struct panthor_group *group)
> +{
> + struct panthor_device *ptdev = sched->ptdev;
> +
> + if (drm_WARN_ON(&ptdev->base, group->csg_id < 0))
> + return;
> +
> + csgs_upd_ctx_queue_reqs(ptdev, upd_ctx, group->csg_id,
> + group_can_run(group) ?
> + CSG_STATE_SUSPEND : CSG_STATE_TERMINATE,
> + CSG_STATE_MASK);
> +}
> +
> +static void
> +tick_ctx_update_group_prio(struct panthor_scheduler *sched,
> + struct panthor_csg_slots_upd_ctx *upd_ctx,
> + struct panthor_group *group,
> + int new_csg_prio)
> +{
> + struct panthor_device *ptdev = sched->ptdev;
> + struct panthor_fw_csg_iface *csg_iface;
> + struct panthor_csg_slot *csg_slot;
> +
> + if (group->csg_id < 0)
> + return;
> +
> + csg_iface = panthor_fw_get_csg_iface(ptdev, group->csg_id);
> + csg_slot = &sched->csg_slots[group->csg_id];
> +
> + if (csg_slot->priority != new_csg_prio) {
> + panthor_fw_csg_endpoint_req_update(ptdev, csg_iface,
> + CSG_EP_REQ_PRIORITY(new_csg_prio),
> + CSG_EP_REQ_PRIORITY_MASK);
> + csgs_upd_ctx_queue_reqs(ptdev, upd_ctx, group->csg_id,
> + csg_iface->output->ack ^ CSG_ENDPOINT_CONFIG,
> + CSG_ENDPOINT_CONFIG);
> + }
> +}
> +
> +static int
> +tick_ctx_schedule_group(struct panthor_scheduler *sched,
> + struct panthor_csg_slots_upd_ctx *upd_ctx,
> + struct panthor_group *group,
> + int csg_id, int csg_prio)
> +{
> + struct panthor_device *ptdev = sched->ptdev;
> + struct panthor_fw_csg_iface *csg_iface =
> + panthor_fw_get_csg_iface(ptdev, csg_id);
> + int ret;
> +
> + ret = group_bind_locked(group, csg_id);
> + if (ret)
> + return ret;
> +
> + csg_slot_prog_locked(ptdev, csg_id, csg_prio);
> +
> + csgs_upd_ctx_queue_reqs(ptdev, upd_ctx, csg_id,
> + group->state == PANTHOR_CS_GROUP_SUSPENDED ?
> + CSG_STATE_RESUME : CSG_STATE_START,
> + CSG_STATE_MASK);
> + csgs_upd_ctx_queue_reqs(ptdev, upd_ctx, csg_id,
> + csg_iface->output->ack ^ CSG_ENDPOINT_CONFIG,
> + CSG_ENDPOINT_CONFIG);
> +
> + return 0;
> +}
> +
> static void
> tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *ctx)
> {
> struct panthor_group *group, *tmp;
> struct panthor_device *ptdev = sched->ptdev;
> - struct panthor_csg_slot *csg_slot;
> int prio, new_csg_prio = MAX_CSG_PRIO, i;
> u32 free_csg_slots = 0;
> struct panthor_csg_slots_upd_ctx upd_ctx;
> @@ -2359,44 +2428,13 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c
>
> for (prio = PANTHOR_CSG_PRIORITY_COUNT - 1; prio >= 0; prio--) {
> /* Suspend or terminate evicted groups. */
> - list_for_each_entry(group, &ctx->old_groups[prio], run_node) {
> - bool term = !group_can_run(group);
> - int csg_id = group->csg_id;
> -
> - if (drm_WARN_ON(&ptdev->base, csg_id < 0))
> - continue;
> -
> - csg_slot = &sched->csg_slots[csg_id];
> - csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id,
> - term ? CSG_STATE_TERMINATE : CSG_STATE_SUSPEND,
> - CSG_STATE_MASK);
> - }
> + list_for_each_entry(group, &ctx->old_groups[prio], run_node)
> + tick_ctx_evict_group(sched, &upd_ctx, group);
>
> /* Update priorities on already running groups. */
> - list_for_each_entry(group, &ctx->groups[prio], run_node) {
> - struct panthor_fw_csg_iface *csg_iface;
> - int csg_id = group->csg_id;
> -
> - if (csg_id < 0) {
> - new_csg_prio--;
> - continue;
> - }
> -
> - csg_slot = &sched->csg_slots[csg_id];
> - csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id);
> - if (csg_slot->priority == new_csg_prio) {
> - new_csg_prio--;
> - continue;
> - }
> -
> - panthor_fw_csg_endpoint_req_update(ptdev, csg_iface,
> - CSG_EP_REQ_PRIORITY(new_csg_prio),
> - CSG_EP_REQ_PRIORITY_MASK);
> - csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id,
> - csg_iface->output->ack ^ CSG_ENDPOINT_CONFIG,
> - CSG_ENDPOINT_CONFIG);
> - new_csg_prio--;
> - }
> + list_for_each_entry(group, &ctx->groups[prio], run_node)
> + tick_ctx_update_group_prio(sched, &upd_ctx, group,
> + new_csg_prio--);
> }
>
> ret = csgs_upd_ctx_apply_locked(ptdev, &upd_ctx);
> @@ -2424,34 +2462,23 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c
> for (prio = PANTHOR_CSG_PRIORITY_COUNT - 1; prio >= 0; prio--) {
> list_for_each_entry(group, &ctx->groups[prio], run_node) {
> int csg_id = group->csg_id;
> - struct panthor_fw_csg_iface *csg_iface;
> + int csg_prio = new_csg_prio--;
>
> - if (csg_id >= 0) {
> - new_csg_prio--;
> + if (csg_id >= 0)
> continue;
> - }
>
> csg_id = ffs(free_csg_slots) - 1;
> if (drm_WARN_ON(&ptdev->base, csg_id < 0))
> break;
>
> - csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id);
> - csg_slot = &sched->csg_slots[csg_id];
> - ret = group_bind_locked(group, csg_id);
> + ret = tick_ctx_schedule_group(sched, &upd_ctx, group,
> + csg_id, csg_prio);
> if (ret) {
> panthor_device_schedule_reset(ptdev);
> ctx->csg_upd_failed_mask |= BIT(csg_id);
> return;
> }
>
> - csg_slot_prog_locked(ptdev, csg_id, new_csg_prio--);
> - csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id,
> - group->state == PANTHOR_CS_GROUP_SUSPENDED ?
> - CSG_STATE_RESUME : CSG_STATE_START,
> - CSG_STATE_MASK);
> - csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id,
> - csg_iface->output->ack ^ CSG_ENDPOINT_CONFIG,
> - CSG_ENDPOINT_CONFIG);
> free_csg_slots &= ~BIT(csg_id);
> }
> }
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v3 6/6] drm/panthor: Expose protected rendering features
2026-09-11 11:40 ` [PATCH v3 6/6] drm/panthor: Expose protected rendering features Ketil Johnsen
2026-09-11 11:55 ` sashiko-bot
@ 2026-09-11 14:42 ` Boris Brezillon
1 sibling, 0 replies; 11+ messages in thread
From: Boris Brezillon @ 2026-09-11 14:42 UTC (permalink / raw)
To: Ketil Johnsen
Cc: Steven Price, Liviu Dudau, Maarten Lankhorst, Maxime Ripard,
Thomas Zimmermann, David Airlie, Simona Vetter, Daniel Almeida,
Alice Ryhl, dri-devel, linux-kernel
On Fri, 11 Sep 2026 13:40:14 +0200
Ketil Johnsen <ketil.johnsen@arm.com> wrote:
> From: Boris Brezillon <boris.brezillon@collabora.com>
>
> Extensions to Panthor uAPI:
> - New IOCTL for user space to provide protected FW memory.
> - New query for checking protected rendering availability/status
> and requirements.
> - Extends group creation to allow user space to provide a protected
> suspend buffer.
>
> The Mali GPU FW needs some protected memory when executing in protected
> mode. This FW memory section is assigned a VA during device init.
> A user space process with the needed privileges (CAP_SYS_MODULE) must
> provide a suitable memory buffer before the Mali GPU is capable of
> executing in protected mode.
>
> Processes who want to execute in protected mode must also ensure they
> pass a protected suspend buffer during group creation.
>
> Added panthor_kernel_bo_import() to allow user provided buffers.
> Refactor panthor_kernel_bo_create() to allow shared code with the
> new import variant.
There's just two many things happening here, so I'd suggest splitting
this patch into:
- Add the section_vm_map_flags() helper
- Add size/VA to panthor_fw_section
- s/panthor_gem_debugfs_set_usage_flags/panthor_gem_debugfs_add_usage_flags/
- support creating kernel BOs from a pre-existing GEM object
- add support for FW PROTM init (with the new ioctl)
- add support for PROTM group init
- bump the driver version to expose the new ioctls
> /**
> - * panthor_kernel_bo_create() - Create and map a GEM object to a VM
> + * panthor_kernel_bo_import() - Create a kernel BO from an existing GEM object
> * @ptdev: Device.
> * @vm: VM to map the GEM to.
> - * @size: Size of the buffer object.
> - * @bo_flags: Combination of drm_panthor_bo_flags flags.
> + * @bo: BO to use for our kernel BO.
> * @vm_map_flags: Combination of drm_panthor_vm_bind_op_flags (only those
> * that are related to map operations).
> * @gpu_va: GPU address assigned when mapping to the VM.
> * If gpu_va == PANTHOR_VM_KERNEL_AUTO_VA, the virtual address will be
> * automatically allocated.
> - * @name: Descriptive label of the BO's contents
> + * @vm_map_size: Size of the BO to map to the VM.
> *
> * Return: A valid pointer in case of success, an ERR_PTR() otherwise.
> */
> struct panthor_kernel_bo *
> -panthor_kernel_bo_create(struct panthor_device *ptdev, struct panthor_vm *vm,
> - size_t size, u32 bo_flags, u32 vm_map_flags,
> - u64 gpu_va, const char *name)
> +panthor_kernel_bo_import(struct panthor_device *ptdev, struct panthor_vm *vm,
Not sure I like the name, because the BO we pass is not necessarily
imported. I think I prefer panthor_kernel_bo_create_{with,from}_bo() or
_{with,from}_gem().
> + struct panthor_gem_object *bo, u32 vm_map_flags,
> + u64 gpu_va, u32 vm_map_size)
> {
[..]
> diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c
> index 1fe77e5c41995..d73fc2f6633a6 100644
> --- a/drivers/gpu/drm/panthor/panthor_sched.c
> +++ b/drivers/gpu/drm/panthor/panthor_sched.c
> @@ -1429,10 +1429,15 @@ cs_slot_process_protm_pending_event_locked(struct panthor_device *ptdev,
> if (!group)
> return;
>
> - /* Do not allow user space work to switch into protected mode, as we
> - * do not fully support this quite yet.
> + /* Do not allow user space work to switch into protected mode if we
> + * do not support protected mode on this device.
> + * User space should query (and init) this support before attempting
> + * to use such GPU instructions.
> */
> - atomic_or(BIT(cs_id), &group->fatal_queues);
> + if (!(ptdev->protm.info.state & DRM_PANTHOR_PROTM_INITIALIZED))
Should we instead check that the group is initialized for PROTM
support, and then have a check in group init to reject group PROTM init
if the device itself is not PROTM-initialized.
> + atomic_or(BIT(cs_id), &group->fatal_queues);
> + else
> + atomic_or(BIT(cs_id), &group->protm_pending_queues);
>
> sched_queue_delayed_work(sched, tick, 0);
> }
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-09-11 14:42 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-11 11:40 [PATCH v3 0/6] drm/panthor: Protected mode support for Mali CSF GPUs Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 1/6] drm/panthor: De-duplicate FW memory section sync Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 2/6] drm/panthor: Minor scheduler refactoring Ketil Johnsen
2026-09-11 14:23 ` Boris Brezillon
2026-09-11 11:40 ` [PATCH v3 3/6] drm/panthor: Pass drm_file instead of panthor_file Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 4/6] drm/panthor: Don't allocate protm_suspend_buf Ketil Johnsen
2026-09-11 11:40 ` [PATCH v3 5/6] drm/panthor: Add support for entering and exiting protected mode Ketil Johnsen
2026-09-11 11:56 ` sashiko-bot
2026-09-11 11:40 ` [PATCH v3 6/6] drm/panthor: Expose protected rendering features Ketil Johnsen
2026-09-11 11:55 ` sashiko-bot
2026-09-11 14:42 ` Boris Brezillon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox