Skip to content

Commit fc11010

Browse files
cris-masudeep-holla
authored andcommitted
firmware: arm_scmi: Add support for multiple vendors custom protocols
Add a mechanism to be able to tag vendor protocol modules at compile-time with a vendor/sub_vendor string and an implementation version and then to choose to load, at run-time, only those vendor protocol modules matching as close as possible the vendor/subvendor identification advertised by the SCMI platform server. In this way, any in-tree existent vendor protocol module can be build and shipped by default in a single kernel image, even when using the same clashing protocol identification numbers, since the SCMI core will take care at run-time to load only the ones pertinent to the running system. Signed-off-by: Cristian Marussi <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sudeep Holla <[email protected]>
1 parent 4625810 commit fc11010

File tree

2 files changed

+162
-22
lines changed

2 files changed

+162
-22
lines changed

drivers/firmware/arm_scmi/driver.c

Lines changed: 147 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <linux/processor.h>
3434
#include <linux/refcount.h>
3535
#include <linux/slab.h>
36+
#include <linux/xarray.h>
3637

3738
#include "common.h"
3839
#include "notify.h"
@@ -44,8 +45,7 @@
4445

4546
static DEFINE_IDA(scmi_id);
4647

47-
static DEFINE_IDR(scmi_protocols);
48-
static DEFINE_SPINLOCK(protocol_lock);
48+
static DEFINE_XARRAY(scmi_protocols);
4949

5050
/* List of all SCMI devices active in system */
5151
static LIST_HEAD(scmi_list);
@@ -194,33 +194,141 @@ struct scmi_info {
194194
#define bus_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, bus_nb)
195195
#define req_nb_to_scmi_info(nb) container_of(nb, struct scmi_info, dev_req_nb)
196196

197-
static const struct scmi_protocol *scmi_protocol_get(int protocol_id)
197+
static unsigned long
198+
scmi_vendor_protocol_signature(unsigned int protocol_id, char *vendor_id,
199+
char *sub_vendor_id, u32 impl_ver)
198200
{
199-
const struct scmi_protocol *proto;
201+
char *signature, *p;
202+
unsigned long hash = 0;
200203

201-
proto = idr_find(&scmi_protocols, protocol_id);
204+
/* vendor_id/sub_vendor_id guaranteed <= SCMI_SHORT_NAME_MAX_SIZE */
205+
signature = kasprintf(GFP_KERNEL, "%02X|%s|%s|0x%08X", protocol_id,
206+
vendor_id ?: "", sub_vendor_id ?: "", impl_ver);
207+
if (!signature)
208+
return 0;
209+
210+
p = signature;
211+
while (*p)
212+
hash = partial_name_hash(tolower(*p++), hash);
213+
hash = end_name_hash(hash);
214+
215+
kfree(signature);
216+
217+
return hash;
218+
}
219+
220+
static unsigned long
221+
scmi_protocol_key_calculate(int protocol_id, char *vendor_id,
222+
char *sub_vendor_id, u32 impl_ver)
223+
{
224+
if (protocol_id < SCMI_PROTOCOL_VENDOR_BASE)
225+
return protocol_id;
226+
else
227+
return scmi_vendor_protocol_signature(protocol_id, vendor_id,
228+
sub_vendor_id, impl_ver);
229+
}
230+
231+
static const struct scmi_protocol *
232+
__scmi_vendor_protocol_lookup(int protocol_id, char *vendor_id,
233+
char *sub_vendor_id, u32 impl_ver)
234+
{
235+
unsigned long key;
236+
struct scmi_protocol *proto = NULL;
237+
238+
key = scmi_protocol_key_calculate(protocol_id, vendor_id,
239+
sub_vendor_id, impl_ver);
240+
if (key)
241+
proto = xa_load(&scmi_protocols, key);
242+
243+
return proto;
244+
}
245+
246+
static const struct scmi_protocol *
247+
scmi_vendor_protocol_lookup(int protocol_id, char *vendor_id,
248+
char *sub_vendor_id, u32 impl_ver)
249+
{
250+
const struct scmi_protocol *proto = NULL;
251+
252+
/* Searching for closest match ...*/
253+
proto = __scmi_vendor_protocol_lookup(protocol_id, vendor_id,
254+
sub_vendor_id, impl_ver);
255+
if (proto)
256+
return proto;
257+
258+
/* Any match just on vendor/sub_vendor ? */
259+
if (impl_ver) {
260+
proto = __scmi_vendor_protocol_lookup(protocol_id, vendor_id,
261+
sub_vendor_id, 0);
262+
if (proto)
263+
return proto;
264+
}
265+
266+
/* Any match just on the vendor ? */
267+
if (sub_vendor_id)
268+
proto = __scmi_vendor_protocol_lookup(protocol_id, vendor_id,
269+
NULL, 0);
270+
return proto;
271+
}
272+
273+
static const struct scmi_protocol *
274+
scmi_protocol_get(int protocol_id, struct scmi_revision_info *version)
275+
{
276+
const struct scmi_protocol *proto = NULL;
277+
278+
if (protocol_id < SCMI_PROTOCOL_VENDOR_BASE)
279+
proto = xa_load(&scmi_protocols, protocol_id);
280+
else
281+
proto = scmi_vendor_protocol_lookup(protocol_id,
282+
version->vendor_id,
283+
version->sub_vendor_id,
284+
version->impl_ver);
202285
if (!proto || !try_module_get(proto->owner)) {
203286
pr_warn("SCMI Protocol 0x%x not found!\n", protocol_id);
204287
return NULL;
205288
}
206289

207290
pr_debug("Found SCMI Protocol 0x%x\n", protocol_id);
208291

292+
if (protocol_id >= SCMI_PROTOCOL_VENDOR_BASE)
293+
pr_info("Loaded SCMI Vendor Protocol 0x%x - %s %s %X\n",
294+
protocol_id, proto->vendor_id ?: "",
295+
proto->sub_vendor_id ?: "", proto->impl_ver);
296+
209297
return proto;
210298
}
211299

212-
static void scmi_protocol_put(int protocol_id)
300+
static void scmi_protocol_put(const struct scmi_protocol *proto)
213301
{
214-
const struct scmi_protocol *proto;
215-
216-
proto = idr_find(&scmi_protocols, protocol_id);
217302
if (proto)
218303
module_put(proto->owner);
219304
}
220305

306+
static int scmi_vendor_protocol_check(const struct scmi_protocol *proto)
307+
{
308+
if (!proto->vendor_id) {
309+
pr_err("missing vendor_id for protocol 0x%x\n", proto->id);
310+
return -EINVAL;
311+
}
312+
313+
if (strlen(proto->vendor_id) >= SCMI_SHORT_NAME_MAX_SIZE) {
314+
pr_err("malformed vendor_id for protocol 0x%x\n", proto->id);
315+
return -EINVAL;
316+
}
317+
318+
if (proto->sub_vendor_id &&
319+
strlen(proto->sub_vendor_id) >= SCMI_SHORT_NAME_MAX_SIZE) {
320+
pr_err("malformed sub_vendor_id for protocol 0x%x\n",
321+
proto->id);
322+
return -EINVAL;
323+
}
324+
325+
return 0;
326+
}
327+
221328
int scmi_protocol_register(const struct scmi_protocol *proto)
222329
{
223330
int ret;
331+
unsigned long key;
224332

225333
if (!proto) {
226334
pr_err("invalid protocol\n");
@@ -232,12 +340,23 @@ int scmi_protocol_register(const struct scmi_protocol *proto)
232340
return -EINVAL;
233341
}
234342

235-
spin_lock(&protocol_lock);
236-
ret = idr_alloc(&scmi_protocols, (void *)proto,
237-
proto->id, proto->id + 1, GFP_ATOMIC);
238-
spin_unlock(&protocol_lock);
239-
if (ret != proto->id) {
240-
pr_err("unable to allocate SCMI idr slot for 0x%x - err %d\n",
343+
if (proto->id >= SCMI_PROTOCOL_VENDOR_BASE &&
344+
scmi_vendor_protocol_check(proto))
345+
return -EINVAL;
346+
347+
/*
348+
* Calculate a protocol key to register this protocol with the core;
349+
* key value 0 is considered invalid.
350+
*/
351+
key = scmi_protocol_key_calculate(proto->id, proto->vendor_id,
352+
proto->sub_vendor_id,
353+
proto->impl_ver);
354+
if (!key)
355+
return -EINVAL;
356+
357+
ret = xa_insert(&scmi_protocols, key, (void *)proto, GFP_KERNEL);
358+
if (ret) {
359+
pr_err("unable to allocate SCMI protocol slot for 0x%x - err %d\n",
241360
proto->id, ret);
242361
return ret;
243362
}
@@ -250,9 +369,15 @@ EXPORT_SYMBOL_GPL(scmi_protocol_register);
250369

251370
void scmi_protocol_unregister(const struct scmi_protocol *proto)
252371
{
253-
spin_lock(&protocol_lock);
254-
idr_remove(&scmi_protocols, proto->id);
255-
spin_unlock(&protocol_lock);
372+
unsigned long key;
373+
374+
key = scmi_protocol_key_calculate(proto->id, proto->vendor_id,
375+
proto->sub_vendor_id,
376+
proto->impl_ver);
377+
if (!key)
378+
return;
379+
380+
xa_erase(&scmi_protocols, key);
256381

257382
pr_debug("Unregistered SCMI Protocol 0x%x\n", proto->id);
258383
}
@@ -1940,7 +2065,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
19402065
/* Protocol specific devres group */
19412066
gid = devres_open_group(handle->dev, NULL, GFP_KERNEL);
19422067
if (!gid) {
1943-
scmi_protocol_put(proto->id);
2068+
scmi_protocol_put(proto);
19442069
goto out;
19452070
}
19462071

@@ -2004,7 +2129,7 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info,
20042129

20052130
clean:
20062131
/* Take care to put the protocol module's owner before releasing all */
2007-
scmi_protocol_put(proto->id);
2132+
scmi_protocol_put(proto);
20082133
devres_release_group(handle->dev, gid);
20092134
out:
20102135
return ERR_PTR(ret);
@@ -2038,7 +2163,7 @@ scmi_get_protocol_instance(const struct scmi_handle *handle, u8 protocol_id)
20382163
const struct scmi_protocol *proto;
20392164

20402165
/* Fails if protocol not registered on bus */
2041-
proto = scmi_protocol_get(protocol_id);
2166+
proto = scmi_protocol_get(protocol_id, &info->version);
20422167
if (proto)
20432168
pi = scmi_alloc_init_protocol_instance(info, proto);
20442169
else
@@ -2093,7 +2218,7 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id)
20932218

20942219
idr_remove(&info->protocols, protocol_id);
20952220

2096-
scmi_protocol_put(protocol_id);
2221+
scmi_protocol_put(pi->proto);
20972222

20982223
devres_release_group(handle->dev, gid);
20992224
dev_dbg(handle->dev, "De-Initialized protocol: 0x%X\n",

drivers/firmware/arm_scmi/protocols.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
#define PROTOCOL_REV_MAJOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MAJOR_MASK, (x))))
3030
#define PROTOCOL_REV_MINOR(x) ((u16)(FIELD_GET(PROTOCOL_REV_MINOR_MASK, (x))))
3131

32+
#define SCMI_PROTOCOL_VENDOR_BASE 0x80
33+
3234
enum scmi_common_cmd {
3335
PROTOCOL_VERSION = 0x0,
3436
PROTOCOL_ATTRIBUTES = 0x1,
@@ -323,6 +325,16 @@ typedef int (*scmi_prot_init_ph_fn_t)(const struct scmi_protocol_handle *);
323325
* protocol by the agent. Each protocol implementation
324326
* in the agent is supposed to downgrade to match the
325327
* protocol version supported by the platform.
328+
* @vendor_id: A firmware vendor string for vendor protocols matching.
329+
* Ignored when @id identifies a standard protocol, cannot be NULL
330+
* otherwise.
331+
* @sub_vendor_id: A firmware sub_vendor string for vendor protocols matching.
332+
* Ignored if NULL or when @id identifies a standard protocol.
333+
* @impl_ver: A firmware implementation version for vendor protocols matching.
334+
* Ignored if zero or if @id identifies a standard protocol.
335+
*
336+
* Note that vendor protocols matching at load time is performed by attempting
337+
* the closest match first against the tuple (vendor, sub_vendor, impl_ver)
326338
*/
327339
struct scmi_protocol {
328340
const u8 id;
@@ -332,6 +344,9 @@ struct scmi_protocol {
332344
const void *ops;
333345
const struct scmi_protocol_events *events;
334346
unsigned int supported_version;
347+
char *vendor_id;
348+
char *sub_vendor_id;
349+
u32 impl_ver;
335350
};
336351

337352
#define DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(name, proto) \

0 commit comments

Comments
 (0)