| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. | |
| */ | |
| #include <linux/delay.h> | |
| #include "qcomtee.h" | |
| /** | |
| * DOC: Primordial Object | |
| * | |
| * After boot, the kernel provides a static object of type | |
| * %QCOMTEE_OBJECT_TYPE_CB called the primordial object. This object is used | |
| * for native kernel services or privileged operations. | |
| * | |
| * We support: | |
| * - %QCOMTEE_OBJECT_OP_MAP_REGION to map a memory object and return mapping | |
| * object and mapping information (see qcomtee_mem_object_map()). | |
| * - %QCOMTEE_OBJECT_OP_YIELD to yield by the thread running in QTEE. | |
| * - %QCOMTEE_OBJECT_OP_SLEEP to wait for a period of time. | |
| */ | |
| #define QCOMTEE_OBJECT_OP_MAP_REGION 0 | |
| #define QCOMTEE_OBJECT_OP_YIELD 1 | |
| #define QCOMTEE_OBJECT_OP_SLEEP 2 | |
| /* Mapping information format as expected by QTEE. */ | |
| struct qcomtee_mapping_info { | |
| u64 paddr; | |
| u64 len; | |
| u32 perms; | |
| } __packed; | |
| static int | |
| qcomtee_primordial_obj_dispatch(struct qcomtee_object_invoke_ctx *oic, | |
| struct qcomtee_object *primordial_object_unused, | |
| u32 op, struct qcomtee_arg *args) | |
| { | |
| struct qcomtee_mapping_info *map_info; | |
| struct qcomtee_object *mem_object; | |
| struct qcomtee_object *map_object; | |
| int err = 0; | |
| switch (op) { | |
| case QCOMTEE_OBJECT_OP_YIELD: | |
| cond_resched(); | |
| /* No output object. */ | |
| oic->data = NULL; | |
| break; | |
| case QCOMTEE_OBJECT_OP_SLEEP: | |
| /* Check message format matched QCOMTEE_OBJECT_OP_SLEEP op. */ | |
| if (qcomtee_args_len(args) != 1 || | |
| args[0].type != QCOMTEE_ARG_TYPE_IB || | |
| args[0].b.size < sizeof(u32)) | |
| return -EINVAL; | |
| msleep(*(u32 *)(args[0].b.addr)); | |
| /* No output object. */ | |
| oic->data = NULL; | |
| break; | |
| case QCOMTEE_OBJECT_OP_MAP_REGION: | |
| if (qcomtee_args_len(args) != 3 || | |
| args[0].type != QCOMTEE_ARG_TYPE_OB || | |
| args[1].type != QCOMTEE_ARG_TYPE_IO || | |
| args[2].type != QCOMTEE_ARG_TYPE_OO || | |
| args[0].b.size < sizeof(struct qcomtee_mapping_info)) | |
| return -EINVAL; | |
| map_info = args[0].b.addr; | |
| mem_object = args[1].o; | |
| qcomtee_mem_object_map(mem_object, &map_object, | |
| &map_info->paddr, &map_info->len, | |
| &map_info->perms); | |
| args[2].o = map_object; | |
| /* One output object; pass it for cleanup to notify. */ | |
| oic->data = map_object; | |
| qcomtee_object_put(mem_object); | |
| break; | |
| default: | |
| err = -EINVAL; | |
| } | |
| return err; | |
| } | |
| /* Called after submitting the callback response. */ | |
| static void qcomtee_primordial_obj_notify(struct qcomtee_object_invoke_ctx *oic, | |
| struct qcomtee_object *unused, | |
| int err) | |
| { | |
| struct qcomtee_object *object = oic->data; | |
| /* If err, QTEE did not obtain mapping object. Drop it. */ | |
| if (object && err) | |
| qcomtee_object_put(object); | |
| } | |
| static struct qcomtee_object_operations qcomtee_primordial_obj_ops = { | |
| .dispatch = qcomtee_primordial_obj_dispatch, | |
| .notify = qcomtee_primordial_obj_notify, | |
| }; | |
| struct qcomtee_object qcomtee_primordial_object = { | |
| .name = "primordial", | |
| .object_type = QCOMTEE_OBJECT_TYPE_CB, | |
| .ops = &qcomtee_primordial_obj_ops | |
| }; |