Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: add new PRC and PTA for loadable plugins framework #4248

Merged
merged 2 commits into from
Feb 4, 2021

Conversation

anisyanka
Copy link
Contributor

Any external TEE services can be designed as a tee-supplicant plugin.
The plugins will be loaded by the supplicant during startup process
using libdl.
It makes it easy to:

  • add new features in the supplicant that aren't needed in upstream,
    e.g. Rich OS specific services;
  • sync upstream version with own fork;

These patches add a new RPC - 'OPTEE_RPC_CMD_PLUGIN' as an unified
interface between OP-TEE and any plugins and a new PTA as an unified
interface between any user's TAs and any plugins.

Kernel code can use RPC directly to call for execution of some command in plugins.
TAs can use the plugin-pta.

Every plugin has own name based on UUID.
OP-TEE has access to plugins by it.

See definition of protocol for the command 'OPTEE_RPC_CMD_PLUGIN'
in 'core/include/optee_rpc_cmd.h' file.

Signed-off-by: Aleksandr Anisimov a.anisimov@omprussia.ru

Copy link
Contributor

@jforissier jforissier left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @anisyanka,

Thanks for the patches. Please see my comments below.

@@ -139,6 +139,11 @@
*/
#define OPTEE_RPC_CMD_FTRACE 11

/*
* Plugin command, see definition of protocol below
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tee-supplicant plugin command ...

/*
* Plugin command, see definition of protocol below
*/
#define OPTEE_RPC_CMD_PLUGIN 12
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OPTEE_RPC_CMD_SUPP_PLUGIN

* [in] value[2].a sub_cmd for plugin
* [in] memref[3] buffer holding data for plugin
*/
#define OPTEE_INVOKE_PLUGIN 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency: OPTEE_RPC_SUPP_PLUGIN_INVOKE

* [in] value[1].b uuid.d4
* [in] value[1].c cmd for plugin
* [in] value[2].a sub_cmd for plugin
* [in] memref[3] buffer holding data for plugin
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any chance one would need to return data back to secure world? ([in/out]).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think such cases are possible and we should provide the [in/out] ability, thanks

* Returns TEE_SUCCESS, when successfully converting a string or an error code,
* if it has a wrong format or NULL.
*/
TEE_Result tee_uuid_from_string(TEE_UUID *dst, const char *str, int len);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This duplicates tee_uuid_from_str() from lib/libutee/tee_uuid_from_str.c. If we keep string UUIDs (which I'm not convinced we should), then the implementation could be moved to libutils I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow, I will try to use the tee_uuid_from_str() from libutee :)
In this case I will drop own implementation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I said even better would be to not use character strings at all and use a TEE_UUID instead, unless I am missing something?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TAs work with plugins by its names and that's why the TAs pass the string with name to TEE_InvokeTACommand(), like here. This string is passed to the plugin-pta.
We can use tee_uuid_from_str() inside the PTA before calling tee_invoke_plugin_rpc().
If we will not use character strings at all in optee, we will need convert the strings in TA, when we want to call plugins.
I don't mind, but seems it looks not convinient for TA developes. What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could do:

#define SYSLOG_PLUGIN_UUID { 0x96bcf744, 0x4f72, 0x4866, \
                         { 0xbf, 0x1d, 0x86, 0x34, 0xfd, 0x9c, 0x65, 0xe5 } }

...instead of:

#define SYSLOG_PLUGIN_NAME "96bcf744-4f72-4866-bf1d-8634fd9c65e5"

That's how TAs open other TAs or PTAs for example. The only place you may need to convert from binary UUIDs to a string is in tee-supplicant when looking for a plugin (and even in this case it may not be necessary, I mean assuming tee-supplicant enumerates and loads all the plugins on startup, it would convert from plugin name to UUID instead, or find the UUID in the plugin).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, got it, thanks!

#include <tee_api_types.h>

struct tee_plugin_rpc_operation {
const char *name; /* string "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use a TEE_UUID?

core/tee/sub.mk Outdated
@@ -45,3 +45,4 @@ endif #CFG_WITH_USER_TA,y

srcs-y += uuid.c
srcs-y += tee_ta_enc_manager.c
srcs-y += tee_plugin_rpc.c
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tee_supp_plugin_rpc.c

#include <stdint.h>
#include <tee_api_types.h>

struct tee_plugin_rpc_operation {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"supplicant" is missing here, struct supp_plugin_rpc_op maybe?
And filename could be supp_plugin_rpc.h.


res = tee_uuid_from_string(&u, op->name, strlen(op->name));
if (res) {
EMSG("can't convert string <%s> to UUID", op->name);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You wouldn't need to care about this if you used TEE_UUID.

core/pta/sub.mk Outdated
@@ -10,5 +10,6 @@ endif
srcs-$(CFG_WITH_STATS) += stats.c
srcs-$(CFG_SYSTEM_PTA) += system.c
srcs-$(CFG_NXP_SE05X) += scp03.c
srcs-$(CFG_PLUGIN_PTA) += plugin.c
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CFG_SUPP_PLUGIN_PTA += supp_plugin.c

@etienne-lms
Copy link
Contributor

Isn't this providing the same kind of services as proposed by the OCALLs P-Rs (#3673 and related)?

@anisyanka
Copy link
Contributor Author

Isn't this providing the same kind of services as proposed by the OCALLs P-Rs (#3673 and related)?

Thanks for the link. I looked those PRs. The goals of our PRs are similar, but there are important differences:

  • OCALLs is about an interaction TA with a particular CA, but plugins can be common for several TAs without opening a session.
    The tee-supplicant loads all plugins using libdl from special directory during startup and after that all plugins are alive.
    For example see the syslog plugin (file tee-supplicant/plugins/syslog/syslog_plugin.c in tee-supplicant: add a framework for loadable plugins optee_client#239). With the help of it any TAs can write something to system log.
  • The proposed plugins don't require changes in Linux kernel and in libteec API. That's why plugins look more easy, but they still have the same capabilities.

Copy link
Contributor

@jenswi-linaro jenswi-linaro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it make sense to add this to the system PTA instead of defining yet another PTA? System PTA is about providing system services to user TAs so it seems like a good match.

goto out;
}

memcpy(&plug_uuid, &u, sizeof(TEE_UUID));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe tee_uuid_to_octets() would be a better choice in order to get the correct byte order.

uint32_t d2;
uint32_t d3;
uint32_t d4;
} plug_uuid;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like uint32_t plug_uuid[4] = { }; would be just as useful and a bit simpler.


memcpy(&plug_uuid, &u, sizeof(TEE_UUID));

struct thread_param params[THREAD_RPC_MAX_NUM_PARAMS] = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declarations of variables at the start of the block please.

* [in] value[1].b sub_command for plugin
* [in] memref[2] additional data for plugin
*/
#define PTA_PLUGIN_INVOKE 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be nice with a wrapper in libutee, something like:

TEE_Result optee_invoke_supp_plugin(const TEE_UUID *uuid, uint32_t cmd, uint32_t sub_cmd,
                                    void *buf, size_t len);

* [in] memref[0].size length of the uuid string including '\0' symbol
* [in] value[1].a command for plugin
* [in] value[1].b sub_command for plugin
* [in] memref[2] additional data for plugin
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be in and out?

@anisyanka
Copy link
Contributor Author

Wouldn't it make sense to add this to the system PTA instead of defining yet another PTA? System PTA is about providing system services to user TAs so it seems like a good match.

Sounds good. I will move core/pta/supp_plugin.c functionality to core/pta/system and add a new API like #4248 (comment) to lib/libutee/tee_system_pta.c

@anisyanka
Copy link
Contributor Author

All comments have been addressed

Copy link
Contributor

@etienne-lms etienne-lms left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor comments

*/

#ifndef TEE_PLUGIN_RPC_H
#define TEE_PLUGIN_RPC_H
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TEE_SUPP_PLUGIN_RPC_H

static TEE_Result system_supp_plugin_invoke(uint32_t param_types,
TEE_Param params[TEE_NUM_PARAMS])
{
struct tee_supp_plugin_rpc_op op;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

= { };

#include <tee/tee_supp_plugin_rpc.h>
#include <tee/uuid.h>
#include <trace.h>
#include <mm/mobj.h>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move before line 8

{
TEE_Result res = TEE_ERROR_GENERIC;
struct thread_param params[THREAD_RPC_MAX_NUM_PARAMS];
uint32_t plugin_uuid[4];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

= { 0 };

uint32_t plugin_uuid[4];
void *va = NULL;
struct mobj *mobj = NULL;
uint32_t outlen;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

= 0;

res = invoke_system_pta(PTA_SYSTEM_SUPP_PLUGIN_INVOKE, param_types,
params);
if (res)
EMSG("Invoke tee-supplicant's plugin failed");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would make it a DMSG():

		DMSG("Invoke tee-supplicant's plugin failed: %#"PRIx32, res);

Copy link
Contributor Author

@anisyanka anisyanka Dec 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please explain why you want to use DMSG() instead EMSG()?
In this case we print the error message, that's why it seems EMSG is more suitable.

core/pta/system.c Outdated Show resolved Hide resolved
/*
* Invoke a tee-supplicant's plugin
*
* [in] memref[0] uuid(octets) of the plugin
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Specify that it's a TEE_UUID to be clear.

* @cmd: command for the plugin
* @sub_cmd: subcommand for the plugin
* @buf: data [for/from] the plugin [in/out]
* @len: length of the buf
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be an out parameter too, in case the size is smaller when returning some result?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think yes.
We can replace size_t len to size_t *len and use one pointer to input and output length.

#include <trace.h>
#include <mm/mobj.h>

TEE_Result tee_invoke_supp_plugin_rpc(struct tee_supp_plugin_rpc_op *op)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this function taking a struct tee_supp_plugin_rpc_op only used for this purpose instead of separate parameters?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can use separate parameters here. Ok, I will drop tee_supp_plugin_rpc_op

@anisyanka
Copy link
Contributor Author

All comments have been addressed

params[1].value.b, /* sub_cmd */
params[2].memref.buffer, /* data */
params[2].memref.size, /* in len */
(size_t *)&params[3].value.a
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cast isn't portable.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right. I will fix it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry, I should have been more clear. Please use a temporary helper variable instead of changing the type in the argument of tee_invoke_supp_plugin_rpc().

Copy link
Contributor Author

@anisyanka anisyanka Jan 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems you see outdated changes.
In previous commits I changed outlen type for tee_invoke_supp_plugin_rpc() from size_t * to uint32_t * and removed type conversion in core/pta/system.c:863. See the last fixup commit: 3a46040#diff-80ee4f614b595d036997a8c22e8034f37acf85250c5829593e630cab71a9bb4aR863.

I can return back size_t * and add temporary helper variable, if it will be better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can return back size_t * and add temporary helper variable, if it will be better.

Please do so.

memcpy(va, buf, len);
}

plugin_uuid = (uint32_t *)uuid;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you need to do something like:

uint32_t uuid_words[4] = { };
tee_uuid_to_octets(uuid_words, uuid);

Secure world is always in little endian so there's no need for any more byte swapping.

Later when decoding this in tee-supplicant you'd do something like:

TEEC_UUID uuid = { };
uint32_t uuid_words[4] = { };

uuid_words[0] = from_le32(params[0].b);
...
tee_uuid_from_octets(&uuid, (void *)uuid_words);

This should work for both a big and little endian normal world. Other parts in the chain (driver) are missing so a big endian normal world wouldn't work for other reasons, but we shouldn't make it harder than necessary in case we'd ever fix that.

Do it make sense?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it make sense. I added le32toh() to tee-supplicant when decoding uuid_words[].

* [out] value[2].b length of the outbuf (memref[3]), if out is needed.
* [in/out] memref[3] buffer holding data for plugin
*
* Serialized bytes of the TEE_UUID:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be more clear to say: "UUID serialized into octets".

This is not to be confused with the in memory representation of TEE_UUID.

@anisyanka
Copy link
Contributor Author

All comments have been addressed

@jenswi-linaro
Copy link
Contributor

Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>

Aleksandr Anisimov added 2 commits January 25, 2021 12:15
Any external TEE services can be designed as a tee-supplicant plugin.
The plugins will be loaded by the supplicant during startup process
using libdl.
It makes it easy to:
 - add new features in the supplicant that aren't needed in upstream,
   e.g. Rich OS specific services;
 - sync upstream version with own fork;

This patch adds a new RPC - 'OPTEE_RPC_CMD_SUPP_PLUGIN' as an unified
interface between OP-TEE and any plugins. Kernel code can use it
to call for execution of some command in plugins.

Every plugin has own name based on UUID.
OP-TEE has access to plugins by it.

See definition of protocol for the plugin RPC command
in 'core/include/optee_rpc_cmd.h' file.

Signed-off-by: Aleksandr Anisimov <a.anisimov@omprussia.ru>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
This patch adds a new API to libutee to interact
with tee-supplicant plugins from TEE userspace.

Every user TA can use 'tee_invoke_supp_plugin()' to send any commands
to a plugin. The commands are predefined by the plugin developer.

See the https://github.com/linaro-swg/optee_examples
repo for an example of using plugins.

Signed-off-by: Aleksandr Anisimov <a.anisimov@omprussia.ru>
Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org>
@anisyanka
Copy link
Contributor Author

The commits reworded.
Thank you for the review!

@jforissier jforissier merged commit e4ad5cc into OP-TEE:master Feb 4, 2021
@jforissier
Copy link
Contributor

@anisyanka thanks for your contribution.

@anisyanka anisyanka deleted the plugin-pta branch February 4, 2021 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants