hal-backend-power: Fix possible vulnerabilities
[platform/hal/backend/vim3/power-vim3.git] / src / hal-backend-power.c
1 /*
2  * HAL backend for hal-api-power which is used for PASS daemon
3  *
4  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <errno.h>
20 #include <limits.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23
24 #include <hal/hal-power-interface.h>
25
26 #include "sysfs.h"
27
28 #define BUFF_MAX        255
29
30 /*************************
31  * H/W Resource Defintions
32  */
33 #define CPUFREQ_PATH_PREFIX                     "/sys/devices/system/cpu/"
34 #define CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX       "/cpufreq/scaling_governor"
35
36 /*
37  * The cpuinfo_cur_freq indicates the actual operating CPU freqeuncy
38  * and scaling_cur_freq is the CPU frequency set by the CPUFREQ policy.
39  */
40 #define CPUFREQ_CURR_FREQ_PATH_SUFFIX           "/cpufreq/cpuinfo_cur_freq"
41 #define CPUFREQ_AVAILABLE_MIN_FREQ_PATH_SUFFIX  "/cpufreq/cpuinfo_min_freq"
42 #define CPUFREQ_AVAILABLE_MAX_FREQ_PATH_SUFFIX  "/cpufreq/cpuinfo_max_freq"
43 #define CPUFREQ_MIN_FREQ_PATH_SUFFIX            "/cpufreq/scaling_min_freq"
44 #define CPUFREQ_MAX_FREQ_PATH_SUFFIX            "/cpufreq/scaling_max_freq"
45 #define CPUFREQ_UP_THRESHOLD_PATH_SUFFIX        "/cpufreq/ondemand/up_threshold"
46
47 #define CPU_ONLINE_PATH_PREFIX                  "/sys/devices/system/cpu/cpu"
48 #define CPU_ONLINE_PATH_SUFFIX                  "/online"
49 #define CPU_ONLINE_STATE_ON                     1
50 #define CPU_ONLINE_STATE_OFF                    0
51
52 #define TMU_PATH_PREFIX                         "/sys/class/thermal/"
53 #define TMU_TEMP_PATH_SUFFIX                    "/temp"
54 #define TMU_POLICY_PATH_SUFFIX                  "/policy"
55 #define TMU_MAX_TEMP_MASK                       0xFFFFF
56
57 #define FAULT_AROUND_BYTES_PATH                 "/sys/kernel/debug/fault_around_bytes"
58
59 static int get_path(char *path, char *prefix, char *res_name, char *suffix)
60 {
61         int ret;
62
63         if (!path || !prefix || !res_name || !suffix)
64                 return -EINVAL;
65
66         ret = snprintf(path, PATH_MAX, "%s%s%s", prefix, res_name, suffix);
67         if (ret <= 0)
68                 return -EINVAL;
69
70         path[PATH_MAX - 1] = '\0';
71
72         return 0;
73 }
74
75 static int __handle_value_string(int is_write, char *prefix, char *res_name, char *suffix, char *buf)
76 {
77         char path[PATH_MAX];
78         int ret;
79
80         if (!buf)
81                 return -EINVAL;
82
83         ret = get_path(path, prefix, res_name, suffix);
84         if (ret < 0)
85                 return ret;
86
87         if (!!is_write)
88                 ret = sysfs_write_str(path, buf);
89         else
90                 ret = sysfs_read_str(path, buf, BUFF_MAX);
91
92         return (ret < 0) ? ret : 0;
93 }
94
95 static int get_value_string(char *prefix, char *res_name, char *suffix, char *buf)
96 {
97         return __handle_value_string(0, prefix, res_name, suffix, buf);
98 }
99
100 static int set_value_string(char *prefix, char *res_name, char *suffix, char *buf)
101 {
102         return __handle_value_string(1, prefix, res_name, suffix, buf);
103 }
104
105 static int get_value_integer(char *prefix, char *res_name, char *suffix, int *value)
106 {
107         char path[PATH_MAX];
108         int ret;
109
110         if (!value)
111                 return -EINVAL;
112
113         ret = get_path(path, prefix, res_name, suffix);
114         if (ret < 0)
115                 return ret;
116
117         ret = sysfs_read_int(path, value);
118         if (ret < 0)
119                 return ret;
120
121         return 0;
122 }
123
124 static int set_value_integer(char *prefix, char *res_name, char *suffix, int value)
125 {
126         char path[PATH_MAX];
127         int ret;
128
129         ret = get_path(path, prefix, res_name, suffix);
130         if (ret < 0)
131                 return ret;
132
133         ret = sysfs_write_int(path, value);
134         if (ret < 0)
135                 return ret;
136
137         return 0;
138 }
139
140 /*************************************************
141  * HAL backend implementation for CPU H/W Resource
142  */
143 static int cpufreq_dvfs_get_curr_governor(char *res_name, char *governor)
144 {
145         int ret;
146
147         ret = get_value_string(CPUFREQ_PATH_PREFIX, res_name,
148                                 CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX, governor);
149         return (ret < 0) ? ret : 0;
150 }
151
152 static int cpufreq_dvfs_set_curr_governor(char *res_name, char *governor)
153 {
154         int ret;
155
156         ret = set_value_string(CPUFREQ_PATH_PREFIX, res_name,
157                                 CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX, governor);
158         return (ret < 0) ? ret : 0;
159 }
160
161 static int cpufreq_dvfs_get_curr_freq(char *res_name)
162 {
163         int freq, ret;
164
165         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
166                                 CPUFREQ_CURR_FREQ_PATH_SUFFIX, &freq);
167         return (ret < 0) ? ret : freq;
168 }
169
170 static int cpufreq_dvfs_get_min_freq(char *res_name)
171 {
172         int freq, ret;
173
174         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
175                                 CPUFREQ_MIN_FREQ_PATH_SUFFIX, &freq);
176         return (ret < 0) ? ret : freq;
177 }
178
179 static int cpufreq_dvfs_set_min_freq(char *res_name, int freq)
180 {
181         int ret;
182
183         if (freq < 0)
184                 return -EINVAL;
185
186         ret = set_value_integer(CPUFREQ_PATH_PREFIX, res_name,
187                                 CPUFREQ_MIN_FREQ_PATH_SUFFIX, freq);
188         return (ret < 0) ? ret : 0;
189 }
190
191 static int cpufreq_dvfs_get_max_freq(char *res_name)
192 {
193         int freq, ret;
194
195         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
196                                 CPUFREQ_MAX_FREQ_PATH_SUFFIX, &freq);
197         return (ret < 0) ? ret : freq;
198 }
199
200 static int cpufreq_dvfs_set_max_freq(char *res_name, int freq)
201 {
202         int ret;
203
204         if (freq < 0)
205                 return -EINVAL;
206
207         ret = set_value_integer(CPUFREQ_PATH_PREFIX, res_name,
208                                 CPUFREQ_MAX_FREQ_PATH_SUFFIX, freq);
209         return (ret < 0) ? ret : 0;
210 }
211
212 static int cpufreq_dvfs_get_available_min_freq(char *res_name)
213 {
214         int val, ret;
215
216         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
217                                 CPUFREQ_AVAILABLE_MIN_FREQ_PATH_SUFFIX, &val);
218         return (ret < 0) ? ret : val;
219 }
220
221 static int cpufreq_dvfs_get_available_max_freq(char *res_name)
222 {
223         int val, ret;
224
225         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
226                                 CPUFREQ_AVAILABLE_MAX_FREQ_PATH_SUFFIX, &val);
227         return (ret < 0) ? ret : val;
228 }
229
230 static int cpufreq_dvfs_get_up_threshold(char *res_name)
231 {
232         int val, ret;
233
234         ret = get_value_integer(CPUFREQ_PATH_PREFIX, res_name,
235                                 CPUFREQ_UP_THRESHOLD_PATH_SUFFIX, &val);
236         return (ret < 0) ? ret : val;
237 }
238
239 static int cpufreq_dvfs_set_up_threshold(char *res_name, int up_threshold)
240 {
241         int ret;
242
243         if (up_threshold < 0)
244                 return -EINVAL;
245
246         ret = set_value_integer(CPUFREQ_PATH_PREFIX, res_name,
247                                 CPUFREQ_UP_THRESHOLD_PATH_SUFFIX, up_threshold);
248         return (ret < 0) ? ret : 0;
249 }
250
251 static struct pass_resource_dvfs_ops cpufreq_dvfs_ops =  {
252         .get_curr_governor = cpufreq_dvfs_get_curr_governor,
253         .set_curr_governor = cpufreq_dvfs_set_curr_governor,
254         .get_curr_freq = cpufreq_dvfs_get_curr_freq,
255         .get_min_freq = cpufreq_dvfs_get_min_freq,
256         .set_min_freq = cpufreq_dvfs_set_min_freq,
257         .get_max_freq = cpufreq_dvfs_get_max_freq,
258         .set_max_freq = cpufreq_dvfs_set_max_freq,
259         .get_available_min_freq = cpufreq_dvfs_get_available_min_freq,
260         .get_available_max_freq = cpufreq_dvfs_get_available_max_freq,
261         .get_up_threshold = cpufreq_dvfs_get_up_threshold,
262         .set_up_threshold = cpufreq_dvfs_set_up_threshold,
263 };
264
265 static int cpu_hotplug_get_online_state(char *res_name, int cpu)
266 {
267         char path[PATH_MAX];
268         int ret, online;
269
270         if ((!res_name))
271                 return -EINVAL;
272
273         snprintf(path, PATH_MAX, "%s%d%s",
274                 CPU_ONLINE_PATH_PREFIX,
275                 cpu,
276                 CPU_ONLINE_PATH_SUFFIX);
277
278         ret = sysfs_read_int(path, &online);
279         if (ret < 0)
280                 return ret;
281
282         return online;
283 }
284
285 static int cpu_hotplug_set_online_state(char *res_name, int cpu, int on)
286 {
287         char path[PATH_MAX];
288         int ret;
289
290         if ((!res_name))
291                 return -EINVAL;
292         if ((on != CPU_ONLINE_STATE_ON) && (on != CPU_ONLINE_STATE_OFF))
293                 return -EINVAL;
294
295         /*
296          * NOTE: Exynos SoC series cannot turn off the CPU0
297          * because of h/w design. To prevent the critical problem,
298          * if someone try to turn off the CPU0, just return without any
299          * opertaion.
300          */
301         if (on == 0 && cpu == 0) {
302                 return 0;
303         }
304
305         snprintf(path, PATH_MAX, "%s%d%s",
306                 CPU_ONLINE_PATH_PREFIX,
307                 cpu,
308                 CPU_ONLINE_PATH_SUFFIX);
309
310         ret = sysfs_write_int(path, on);
311         if (ret < 0)
312                 return ret;
313
314         return 0;
315 }
316
317 static struct pass_resource_hotplug_ops cpu_hotplus_ops = {
318         .get_online_state = cpu_hotplug_get_online_state,
319         .set_online_state = cpu_hotplug_set_online_state,
320 };
321
322 static int tmu_get_temp(char *res_thermal_name)
323 {
324         int temp, ret;
325
326         ret = get_value_integer(TMU_PATH_PREFIX, res_thermal_name,
327                                 TMU_TEMP_PATH_SUFFIX, &temp);
328         if (ret <  0)
329                 return ret;
330
331         /*
332          * Thermal framework provides the current temperature
333          * as five digits interger like 54430 when temperature is 54.430
334          * degrees centigrade. But, Thermal Monitor in Tizen usually
335          * use two digits interger without decimal point.
336          * So that round temperature value. It constraints the maximume
337          * temperature as 1048 degrees centigrade for preventing integer
338          * overflow. Usually, the embedded device never over 1000 degrees
339          * centigrade.
340          */
341         return (((temp & TMU_MAX_TEMP_MASK) + 500) / 1000);
342 }
343
344 static int tmu_get_policy(char *res_thermal_name, char *policy)
345 {
346         int ret;
347
348         if (!policy)
349                 return -EINVAL;
350
351         ret = get_value_string(TMU_PATH_PREFIX, res_thermal_name,
352                                 TMU_POLICY_PATH_SUFFIX, policy);
353         return (ret < 0) ? ret : 0;
354 }
355
356 static struct pass_resource_tmu_ops tmu_ops = {
357         .get_temp = tmu_get_temp,
358         .get_policy = tmu_get_policy,
359 };
360
361 /*****************************************************
362  * HAL backend implementation for BUS/GPU H/W Resource
363  */
364
365 static int bus_dvfs_get_curr_freq(char *res_name)
366 {
367         char buf[BUFF_MAX + 1];
368         int ret, freq;
369
370         ret = sysfs_read_str("/sys/class/aml_ddr/freq", buf, BUFF_MAX);
371         if (ret < 0)
372                 return ret;
373
374         if (sscanf(buf, "%d %*s", &freq) != 1)
375                 return -EINVAL;
376
377         if (freq < 0 || freq > INT_MAX/1000)
378                 return -EINVAL;
379
380         return (freq * 1000);
381 }
382
383 static int gpu_freq_table[] = {
384         400000,
385         400000,
386         500000,
387         666000,
388         800000,
389 };
390
391 #define ARRAY_LENGTH(array) (sizeof((array))/sizeof((array)[0]))
392
393 static int gpu_dvfs_get_min_freq(char *res_name)
394 {
395         int freq, ret;
396
397         ret = sysfs_read_int("/sys/class/mpgpu/min_freq", &freq);
398
399         if (freq < 0 || freq >= ARRAY_LENGTH(gpu_freq_table))
400                 return -EINVAL;
401
402         return (ret < 0) ? ret : (gpu_freq_table[freq]);
403 }
404
405 static int gpu_dvfs_get_max_freq(char *res_name)
406 {
407         int freq, ret;
408
409         ret = sysfs_read_int("/sys/class/mpgpu/max_freq", &freq);
410         if (freq < 0 || freq >= ARRAY_LENGTH(gpu_freq_table))
411                 return -EINVAL;
412
413         return (ret < 0) ? ret : gpu_freq_table[freq];
414 }
415
416 static int gpu_dvfs_get_curr_freq(char *res_name)
417 {
418         int freq, ret;
419
420         ret = sysfs_read_int("/sys/class/mpgpu/cur_freq", &freq);
421
422         return (ret < 0) ? ret : (freq * 1000);
423 }
424
425 static struct pass_resource_dvfs_ops bus_dvfs_ops =  {
426         .get_curr_freq = bus_dvfs_get_curr_freq,
427         .get_min_freq = bus_dvfs_get_curr_freq,
428         .get_max_freq = bus_dvfs_get_curr_freq,
429 };
430
431 static struct pass_resource_dvfs_ops gpu_dvfs_ops =  {
432         .get_curr_freq = gpu_dvfs_get_curr_freq,
433         .get_min_freq = gpu_dvfs_get_min_freq,
434         .get_max_freq = gpu_dvfs_get_max_freq,
435 };
436
437 /****************************************************
438  * HAL backend implementation for Memory H/W Resource
439  */
440 static int memory_get_fault_around_bytes(char *res_name)
441 {
442         int ret, fault_around_bytes;
443
444         if (!res_name)
445                 return -EINVAL;
446
447         ret = sysfs_read_int(FAULT_AROUND_BYTES_PATH, &fault_around_bytes);
448         if (ret < 0)
449                 return ret;
450
451         return fault_around_bytes;
452 }
453
454 static int memory_set_fault_around_bytes(char *res_name,
455                                                 int fault_around_bytes)
456 {
457         int ret;
458
459         if ((!res_name) || (fault_around_bytes <= 0))
460                 return -EINVAL;
461
462         ret = sysfs_write_int(FAULT_AROUND_BYTES_PATH, fault_around_bytes);
463         if (ret < 0)
464                 return ret;
465
466         return 0;
467 }
468
469 /************************
470  * HAL backend power data
471  */
472 static int power_init(void **data)
473 {
474         hal_backend_power_funcs *power_funcs = NULL;
475         struct pass_resource_cpu *cpu = NULL;
476         struct pass_resource_bus *bus = NULL;
477         struct pass_resource_gpu *gpu = NULL;
478         struct pass_resource_memory *memory = NULL;
479         int ret;
480
481         /* Allocate memory */
482         power_funcs = calloc(1, sizeof(hal_backend_power_funcs));
483         if (!power_funcs)
484                 return -ENOMEM;
485
486         cpu = calloc(1, sizeof(struct pass_resource_cpu));
487         if (!cpu) {
488                 ret = -ENOMEM;
489                 goto err_funcs;
490         }
491
492         bus = calloc(1, sizeof(struct pass_resource_bus));
493         if (!bus) {
494                 ret = -ENOMEM;
495                 goto err_cpu;
496         }
497
498         gpu = calloc(1, sizeof(struct pass_resource_gpu));
499         if (!gpu) {
500                 ret = -ENOMEM;
501                 goto err_bus;
502         }
503
504         memory = calloc(1, sizeof(struct pass_resource_memory));
505         if (!memory) {
506                 ret = -ENOMEM;
507                 goto err_gpu;
508         }
509
510         /* Initialize each h/w resource */
511         cpu->dvfs = cpufreq_dvfs_ops;
512         cpu->hotplug = cpu_hotplus_ops;
513         cpu->tmu = tmu_ops;
514
515         bus->dvfs = bus_dvfs_ops;
516         bus->tmu = tmu_ops;
517
518         gpu->dvfs = gpu_dvfs_ops;
519         gpu->tmu = tmu_ops;
520
521         memory->get_fault_around_bytes = memory_get_fault_around_bytes;
522         memory->set_fault_around_bytes = memory_set_fault_around_bytes;
523
524         /* Initialize hal_backend_power_funcs  */
525         power_funcs->cpu = cpu;
526         power_funcs->bus = bus;
527         power_funcs->gpu = gpu;
528         power_funcs->memory = memory;
529
530         *data = (void *)power_funcs;
531
532         return 0;
533
534 err_gpu:
535         if (gpu)
536                 free(gpu);
537 err_bus:
538         if (bus)
539                 free(bus);
540 err_cpu:
541         if (cpu)
542                 free(cpu);
543 err_funcs:
544         free(power_funcs);
545
546         return ret;
547 }
548
549 static int power_exit(void *data)
550 {
551         hal_backend_power_funcs *funcs;
552
553         if (!data)
554                 return -EINVAL;
555
556         funcs = (hal_backend_power_funcs *)data;
557
558         if (funcs->cpu)
559                 free(funcs->cpu);
560         if (funcs->bus)
561                 free(funcs->bus);
562         if (funcs->gpu)
563                 free(funcs->gpu);
564         if (funcs->memory)
565                 free(funcs->memory);
566
567         free(funcs);
568
569         return 0;
570 }
571
572 hal_backend hal_backend_power_data = {
573         .name = "hal-backend-power-vim3",
574         .vendor = "amlogic",
575         .abi_version = HAL_ABI_VERSION_TIZEN_6_5,
576         .init = power_init,
577         .exit = power_exit,
578 };