1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright(c) 2023 Intel Corporation */
3 
4 #include <linux/array_size.h>
5 #include <linux/bitops.h>
6 #include <linux/export.h>
7 #include <linux/pci.h>
8 #include <linux/string.h>
9 #include "adf_cfg.h"
10 #include "adf_cfg_services.h"
11 #include "adf_cfg_strings.h"
12 
13 static const char *const adf_cfg_services[] = {
14 	[SVC_ASYM] = ADF_CFG_ASYM,
15 	[SVC_SYM] = ADF_CFG_SYM,
16 	[SVC_DC] = ADF_CFG_DC,
17 	[SVC_DCC] = ADF_CFG_DCC,
18 };
19 
20 /*
21  * Ensure that the size of the array matches the number of services,
22  * SVC_BASE_COUNT, that is used to size the bitmap.
23  */
24 static_assert(ARRAY_SIZE(adf_cfg_services) == SVC_BASE_COUNT);
25 
26 /*
27  * Ensure that the maximum number of concurrent services that can be
28  * enabled on a device is less than or equal to the number of total
29  * supported services.
30  */
31 static_assert(ARRAY_SIZE(adf_cfg_services) >= MAX_NUM_CONCURR_SVC);
32 
33 /*
34  * Ensure that the number of services fit a single unsigned long, as each
35  * service is represented by a bit in the mask.
36  */
37 static_assert(BITS_PER_LONG >= SVC_BASE_COUNT);
38 
39 /*
40  * Ensure that size of the concatenation of all service strings is smaller
41  * than the size of the buffer that will contain them.
42  */
43 static_assert(sizeof(ADF_CFG_SYM ADF_SERVICES_DELIMITER
44 		     ADF_CFG_ASYM ADF_SERVICES_DELIMITER
45 		     ADF_CFG_DC ADF_SERVICES_DELIMITER
46 		     ADF_CFG_DCC) < ADF_CFG_MAX_VAL_LEN_IN_BYTES);
47 
adf_service_string_to_mask(struct adf_accel_dev * accel_dev,const char * buf,size_t len,unsigned long * out_mask)48 static int adf_service_string_to_mask(struct adf_accel_dev *accel_dev, const char *buf,
49 				      size_t len, unsigned long *out_mask)
50 {
51 	struct adf_hw_device_data *hw_data = GET_HW_DATA(accel_dev);
52 	char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { };
53 	unsigned long mask = 0;
54 	char *substr, *token;
55 	int id, num_svc = 0;
56 
57 	if (len > ADF_CFG_MAX_VAL_LEN_IN_BYTES - 1)
58 		return -EINVAL;
59 
60 	strscpy(services, buf, ADF_CFG_MAX_VAL_LEN_IN_BYTES);
61 	substr = services;
62 
63 	while ((token = strsep(&substr, ADF_SERVICES_DELIMITER))) {
64 		id = sysfs_match_string(adf_cfg_services, token);
65 		if (id < 0)
66 			return id;
67 
68 		if (test_and_set_bit(id, &mask))
69 			return -EINVAL;
70 
71 		if (num_svc++ == MAX_NUM_CONCURR_SVC)
72 			return -EINVAL;
73 	}
74 
75 	if (hw_data->services_supported && !hw_data->services_supported(mask))
76 		return -EINVAL;
77 
78 	*out_mask = mask;
79 
80 	return 0;
81 }
82 
adf_service_mask_to_string(unsigned long mask,char * buf,size_t len)83 static int adf_service_mask_to_string(unsigned long mask, char *buf, size_t len)
84 {
85 	int offset = 0;
86 	int bit;
87 
88 	if (len < ADF_CFG_MAX_VAL_LEN_IN_BYTES)
89 		return -ENOSPC;
90 
91 	for_each_set_bit(bit, &mask, SVC_BASE_COUNT) {
92 		if (offset)
93 			offset += scnprintf(buf + offset, len - offset,
94 					    ADF_SERVICES_DELIMITER);
95 
96 		offset += scnprintf(buf + offset, len - offset, "%s",
97 				    adf_cfg_services[bit]);
98 	}
99 
100 	return 0;
101 }
102 
adf_parse_service_string(struct adf_accel_dev * accel_dev,const char * in,size_t in_len,char * out,size_t out_len)103 int adf_parse_service_string(struct adf_accel_dev *accel_dev, const char *in,
104 			     size_t in_len, char *out, size_t out_len)
105 {
106 	unsigned long mask;
107 	int ret;
108 
109 	ret = adf_service_string_to_mask(accel_dev, in, in_len, &mask);
110 	if (ret)
111 		return ret;
112 
113 	if (!mask)
114 		return -EINVAL;
115 
116 	return adf_service_mask_to_string(mask, out, out_len);
117 }
118 
adf_get_service_mask(struct adf_accel_dev * accel_dev,unsigned long * mask)119 static int adf_get_service_mask(struct adf_accel_dev *accel_dev, unsigned long *mask)
120 {
121 	char services[ADF_CFG_MAX_VAL_LEN_IN_BYTES] = { };
122 	size_t len;
123 	int ret;
124 
125 	ret = adf_cfg_get_param_value(accel_dev, ADF_GENERAL_SEC,
126 				      ADF_SERVICES_ENABLED, services);
127 	if (ret) {
128 		dev_err(&GET_DEV(accel_dev), "%s param not found\n",
129 			ADF_SERVICES_ENABLED);
130 		return ret;
131 	}
132 
133 	len = strnlen(services, ADF_CFG_MAX_VAL_LEN_IN_BYTES);
134 	ret = adf_service_string_to_mask(accel_dev, services, len, mask);
135 	if (ret)
136 		dev_err(&GET_DEV(accel_dev), "Invalid value of %s param: %s\n",
137 			ADF_SERVICES_ENABLED, services);
138 
139 	return ret;
140 }
141 
adf_get_service_enabled(struct adf_accel_dev * accel_dev)142 int adf_get_service_enabled(struct adf_accel_dev *accel_dev)
143 {
144 	unsigned long mask;
145 	int ret;
146 
147 	ret = adf_get_service_mask(accel_dev, &mask);
148 	if (ret)
149 		return ret;
150 
151 	if (test_bit(SVC_SYM, &mask) && test_bit(SVC_ASYM, &mask))
152 		return SVC_SYM_ASYM;
153 
154 	if (test_bit(SVC_SYM, &mask) && test_bit(SVC_DC, &mask))
155 		return SVC_SYM_DC;
156 
157 	if (test_bit(SVC_ASYM, &mask) && test_bit(SVC_DC, &mask))
158 		return SVC_ASYM_DC;
159 
160 	if (test_bit(SVC_SYM, &mask))
161 		return SVC_SYM;
162 
163 	if (test_bit(SVC_ASYM, &mask))
164 		return SVC_ASYM;
165 
166 	if (test_bit(SVC_DC, &mask))
167 		return SVC_DC;
168 
169 	if (test_bit(SVC_DCC, &mask))
170 		return SVC_DCC;
171 
172 	return -EINVAL;
173 }
174 EXPORT_SYMBOL_GPL(adf_get_service_enabled);
175