xref: /optee_os/core/kernel/pm.c (revision 77bdbf67c42209142ef43129e01113d29d9c62f6)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2018, Linaro Limited
4  */
5 
6 #include <keep.h>
7 #include <kernel/panic.h>
8 #include <kernel/pm.h>
9 #include <mm/core_memprot.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <types_ext.h>
13 
14 #define PM_FLAG_SUSPENDED	BIT(0)
15 
16 static struct pm_callback_handle *pm_cb_ref;
17 static size_t pm_cb_count;
18 
19 static void verify_cb_args(struct pm_callback_handle *pm_hdl)
20 {
21 	if (is_unpaged((void *)(vaddr_t)pm_change_state) &&
22 	    (!is_unpaged((void *)(vaddr_t)pm_hdl->callback) ||
23 	     (pm_hdl->handle && !is_unpaged(pm_hdl->handle)))) {
24 		EMSG("PM callbacks mandates unpaged arguments: %p %p",
25 		     (void *)(vaddr_t)pm_hdl->callback, pm_hdl->handle);
26 		panic();
27 	}
28 }
29 
30 void register_pm_cb(struct pm_callback_handle *pm_hdl)
31 {
32 	size_t count = pm_cb_count;
33 	struct pm_callback_handle *ref;
34 
35 	verify_cb_args(pm_hdl);
36 
37 	ref = realloc(pm_cb_ref, sizeof(*ref) * (count + 1));
38 	if (!ref)
39 		panic();
40 
41 	ref[count] = *pm_hdl;
42 	ref[count].flags = 0;
43 
44 	pm_cb_count = count + 1;
45 	pm_cb_ref = ref;
46 }
47 
48 static TEE_Result call_callbacks(enum pm_op op, uint32_t pm_hint,
49 				 enum pm_callback_order order)
50 {
51 	struct pm_callback_handle *hdl;
52 	size_t n;
53 	TEE_Result res;
54 
55 	for (n = 0, hdl = pm_cb_ref; n < pm_cb_count; n++, hdl++) {
56 		if (hdl->order != order ||
57 		    (hdl->flags & PM_FLAG_SUSPENDED) == (op == PM_OP_SUSPEND))
58 			continue;
59 
60 		res = hdl->callback(op, pm_hint, hdl);
61 		if (res)
62 			return res;
63 
64 		if (op == PM_OP_SUSPEND)
65 			hdl->flags |= PM_FLAG_SUSPENDED;
66 		else
67 			hdl->flags &= ~PM_FLAG_SUSPENDED;
68 	}
69 
70 	return TEE_SUCCESS;
71 }
72 
73 TEE_Result pm_change_state(enum pm_op op, uint32_t pm_hint)
74 {
75 	enum pm_callback_order cnt;
76 	TEE_Result res;
77 
78 	switch (op) {
79 	case PM_OP_SUSPEND:
80 		for (cnt = PM_CB_ORDER_DRIVER; cnt < PM_CB_ORDER_MAX; cnt++) {
81 			res = call_callbacks(op, pm_hint, cnt);
82 			if (res)
83 				return res;
84 		}
85 		break;
86 	case PM_OP_RESUME:
87 		for (cnt = PM_CB_ORDER_MAX; cnt > PM_CB_ORDER_DRIVER; cnt--) {
88 			res = call_callbacks(op, pm_hint, cnt - 1);
89 			if (res)
90 				return res;
91 		}
92 		break;
93 	default:
94 		panic();
95 	}
96 
97 	return TEE_SUCCESS;
98 }
99