!113 switchless支持异步调用
From: @houmingyong Reviewed-by: @hzero1996 Signed-off-by: @hzero1996
This commit is contained in:
commit
bf8d8ca924
515
0051-asynchronous-switchless.patch
Normal file
515
0051-asynchronous-switchless.patch
Normal file
@ -0,0 +1,515 @@
|
||||
From f73e925c5bd78dff9c6398f62386c86d1e7aaf01 Mon Sep 17 00:00:00 2001
|
||||
From: modric <wangyu283@huawei.com>
|
||||
Date: Wed, 9 Nov 2022 15:14:08 +0800
|
||||
Subject: [PATCH 2/4] asynchronous switchless
|
||||
|
||||
---
|
||||
inc/common_inc/switchless_defs.h | 3 +-
|
||||
inc/host_inc/enclave.h | 13 ++++++++
|
||||
inc/host_inc/enclave_internal.h | 12 ++++---
|
||||
inc/host_inc/status.h | 4 ++-
|
||||
src/host_src/enclave.c | 18 ++++++++++
|
||||
src/host_src/gp/gp_enclave.c | 57 ++++++++++++++++++++++++++++----
|
||||
src/host_src/gp/gp_uswitchless.c | 44 +++++++++++++++++++++---
|
||||
src/host_src/gp/gp_uswitchless.h | 35 ++++++++++++++++----
|
||||
tools/codegener/Genheader.ml | 29 +++++++++++++++-
|
||||
tools/codegener/Genuntrust.ml | 54 ++++++++++++++++++++++++++++--
|
||||
10 files changed, 242 insertions(+), 27 deletions(-)
|
||||
|
||||
diff --git a/inc/common_inc/switchless_defs.h b/inc/common_inc/switchless_defs.h
|
||||
index 84629c3..b525df0 100644
|
||||
--- a/inc/common_inc/switchless_defs.h
|
||||
+++ b/inc/common_inc/switchless_defs.h
|
||||
@@ -59,7 +59,8 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
volatile uint32_t status;
|
||||
- uint32_t func_id;
|
||||
+ uint16_t func_id;
|
||||
+ uint16_t retval_size;
|
||||
volatile uint64_t ret_val;
|
||||
uint64_t params[0];
|
||||
} sl_task_t;
|
||||
diff --git a/inc/host_inc/enclave.h b/inc/host_inc/enclave.h
|
||||
index 0dde8c3..94aedf4 100644
|
||||
--- a/inc/host_inc/enclave.h
|
||||
+++ b/inc/host_inc/enclave.h
|
||||
@@ -87,6 +87,19 @@ CC_API_SPEC cc_enclave_result_t cc_enclave_create(
|
||||
|
||||
CC_API_SPEC cc_enclave_result_t cc_enclave_destroy(cc_enclave_t *context);
|
||||
|
||||
+/*
|
||||
+ * Summary: Obtains the result of the switchless asynchronous invoking task
|
||||
+ * Parameters:
|
||||
+ * enclave: enclave
|
||||
+ * task_id: id of an asynchronous invoking task
|
||||
+ * retval: accepts the return value, NULL is required for functions of the void type or the return value is ignored
|
||||
+ * Return:
|
||||
+ * CC_SUCCESS, success;
|
||||
+ * CC_ERROR_SWITCHLESS_ASYNC_TASK_UNFINISHED, the asynchronous invoking task is not completed;
|
||||
+ * others failed.
|
||||
+ */
|
||||
+CC_API_SPEC cc_enclave_result_t cc_sl_get_async_result(cc_enclave_t *enclave, int task_id, void *retval);
|
||||
+
|
||||
/*automatic file generation required: aligned bytes*/
|
||||
#define ALIGNMENT_SIZE (2 * sizeof(void*))
|
||||
|
||||
diff --git a/inc/host_inc/enclave_internal.h b/inc/host_inc/enclave_internal.h
|
||||
index 5a8af38..a66d1a3 100644
|
||||
--- a/inc/host_inc/enclave_internal.h
|
||||
+++ b/inc/host_inc/enclave_internal.h
|
||||
@@ -31,7 +31,8 @@ typedef enum _enclave_state {
|
||||
} enclave_state_t;
|
||||
|
||||
typedef struct {
|
||||
- uint32_t func_id;
|
||||
+ uint16_t func_id;
|
||||
+ uint16_t retval_size;
|
||||
uint32_t argc;
|
||||
void *args;
|
||||
} sl_ecall_func_info_t;
|
||||
@@ -66,10 +67,11 @@ struct cc_enclave_ops {
|
||||
const void *ocall_table);
|
||||
|
||||
/* switchless ecall */
|
||||
- cc_enclave_result_t (*cc_sl_ecall_enclave)(cc_enclave_t *enclave,
|
||||
- void *retval,
|
||||
- size_t retval_size,
|
||||
- sl_ecall_func_info_t *func_info);
|
||||
+ cc_enclave_result_t (*cc_sl_ecall_enclave)(cc_enclave_t *enclave, void *retval, sl_ecall_func_info_t *func_info);
|
||||
+
|
||||
+ /* switchless async ecall */
|
||||
+ cc_enclave_result_t (*cc_sl_async_ecall)(cc_enclave_t *enclave, int *task_id, sl_ecall_func_info_t *func_info);
|
||||
+ cc_enclave_result_t (*cc_sl_async_ecall_get_result)(cc_enclave_t *enclave, int task_id, void *retval);
|
||||
|
||||
/* shared memory */
|
||||
void *(*cc_malloc_shared_memory)(cc_enclave_t *enclave, size_t size, bool is_control_buf);
|
||||
diff --git a/inc/host_inc/status.h b/inc/host_inc/status.h
|
||||
index 15de9d1..4f982f8 100644
|
||||
--- a/inc/host_inc/status.h
|
||||
+++ b/inc/host_inc/status.h
|
||||
@@ -165,7 +165,9 @@ typedef enum _enclave_result_t
|
||||
CC_ERROR_SHARED_MEMORY_REPEAT_REGISTER, /* The shared memory is repeatedly registered */
|
||||
CC_ERROR_SHARED_MEMORY_START_ADDR_INVALID, /* Invalid start address of the shared memory */
|
||||
CC_ERROR_SHARED_MEMORY_NOT_REGISTERED, /* Unregistered shared memory */
|
||||
- CC_ERROR_ADDRESS_UNACCESSABLE, /* Memory address is not within enclave */
|
||||
+ CC_ERROR_ADDRESS_UNACCESSABLE, /* Memory address is not within enclave */
|
||||
+ CC_ERROR_SWITCHLESS_INVALID_TASK_ID, /* Invalid invoking task ID */
|
||||
+ CC_ERROR_SWITCHLESS_ASYNC_TASK_UNFINISHED, /* The asynchronous invoking task is not completed */
|
||||
CC_MAXIMUM_ERROR,
|
||||
} cc_enclave_result_t;
|
||||
|
||||
diff --git a/src/host_src/enclave.c b/src/host_src/enclave.c
|
||||
index 2e6a28a..d8b7d35 100644
|
||||
--- a/src/host_src/enclave.c
|
||||
+++ b/src/host_src/enclave.c
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "enclave.h"
|
||||
#include "enclave_log.h"
|
||||
#include "enclave_internal.h"
|
||||
+#include "secgear_defs.h"
|
||||
|
||||
extern list_ops_management g_list_ops;
|
||||
|
||||
@@ -313,3 +314,20 @@ cc_enclave_result_t cc_enclave_destroy(cc_enclave_t *context)
|
||||
|
||||
return CC_SUCCESS;
|
||||
}
|
||||
+
|
||||
+cc_enclave_result_t cc_sl_get_async_result(cc_enclave_t *enclave, int task_id, void *retval)
|
||||
+{
|
||||
+ cc_enclave_result_t ret;
|
||||
+
|
||||
+ if (enclave == NULL || task_id < 0 || !enclave->used_flag) {
|
||||
+ return CC_ERROR_BAD_PARAMETERS;
|
||||
+ }
|
||||
+
|
||||
+ CC_RWLOCK_LOCK_RD(&enclave->rwlock);
|
||||
+
|
||||
+ ret = enclave->list_ops_node->ops_desc->ops->cc_sl_async_ecall_get_result(enclave, task_id, retval);
|
||||
+
|
||||
+ CC_RWLOCK_UNLOCK(&enclave->rwlock);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
\ No newline at end of file
|
||||
diff --git a/src/host_src/gp/gp_enclave.c b/src/host_src/gp/gp_enclave.c
|
||||
index f77cdd8..5345973 100644
|
||||
--- a/src/host_src/gp/gp_enclave.c
|
||||
+++ b/src/host_src/gp/gp_enclave.c
|
||||
@@ -817,10 +817,7 @@ done:
|
||||
return result;
|
||||
}
|
||||
|
||||
-cc_enclave_result_t cc_sl_enclave_call_function(cc_enclave_t *enclave,
|
||||
- void *retval,
|
||||
- size_t retval_size,
|
||||
- sl_ecall_func_info_t *func_info)
|
||||
+cc_enclave_result_t cc_sl_enclave_call_function(cc_enclave_t *enclave, void *retval, sl_ecall_func_info_t *func_info)
|
||||
{
|
||||
if (!uswitchless_is_switchless_enabled(enclave)) {
|
||||
return CC_ERROR_SWITCHLESS_DISABLED;
|
||||
@@ -835,19 +832,67 @@ cc_enclave_result_t cc_sl_enclave_call_function(cc_enclave_t *enclave,
|
||||
return CC_ERROR_SWITCHLESS_TASK_POOL_FULL;
|
||||
}
|
||||
|
||||
- uswitchless_fill_task(enclave, task_index, func_info->func_id, func_info->argc, func_info->args);
|
||||
+ uswitchless_fill_task(enclave, task_index, func_info->func_id, func_info->retval_size, func_info->argc,
|
||||
+ func_info->args);
|
||||
uswitchless_submit_task(enclave, task_index);
|
||||
- cc_enclave_result_t ret = uswitchless_get_task_result(enclave, task_index, retval, retval_size);
|
||||
+ cc_enclave_result_t ret = uswitchless_get_task_result(enclave, task_index, retval);
|
||||
uswitchless_put_idle_task_by_index(enclave, task_index);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
+cc_enclave_result_t cc_sl_async_ecall(cc_enclave_t *enclave, int *task_id, sl_ecall_func_info_t *func_info)
|
||||
+{
|
||||
+ if (task_id == NULL) {
|
||||
+ return CC_ERROR_BAD_PARAMETERS;
|
||||
+ }
|
||||
+
|
||||
+ if (!uswitchless_is_switchless_enabled(enclave)) {
|
||||
+ return CC_ERROR_SWITCHLESS_DISABLED;
|
||||
+ }
|
||||
+
|
||||
+ if (!uswitchless_is_valid_param_num(enclave, func_info->argc)) {
|
||||
+ return CC_ERROR_SWITCHLESS_INVALID_ARG_NUM;
|
||||
+ }
|
||||
+
|
||||
+ int task_index = uswitchless_get_idle_task_index(enclave);
|
||||
+ if (task_index < 0) {
|
||||
+ return CC_ERROR_SWITCHLESS_TASK_POOL_FULL;
|
||||
+ }
|
||||
+
|
||||
+ uswitchless_fill_task(enclave, task_index, func_info->func_id, func_info->retval_size, func_info->argc,
|
||||
+ func_info->args);
|
||||
+ uswitchless_submit_task(enclave, task_index);
|
||||
+ *task_id = task_index;
|
||||
+
|
||||
+ return CC_SUCCESS;
|
||||
+}
|
||||
+
|
||||
+cc_enclave_result_t cc_sl_async_ecall_check_result(cc_enclave_t *enclave, int task_id, void *retval)
|
||||
+{
|
||||
+ if (!uswitchless_is_switchless_enabled(enclave)) {
|
||||
+ return CC_ERROR_SWITCHLESS_DISABLED;
|
||||
+ }
|
||||
+
|
||||
+ if (!uswitchless_is_valid_task_index(enclave, task_id)) {
|
||||
+ return CC_ERROR_SWITCHLESS_INVALID_TASK_ID;
|
||||
+ }
|
||||
+
|
||||
+ cc_enclave_result_t ret = uswitchless_get_async_task_result(enclave, task_id, retval);
|
||||
+ if (ret != CC_ERROR_SWITCHLESS_ASYNC_TASK_UNFINISHED) {
|
||||
+ uswitchless_put_idle_task_by_index(enclave, task_id);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
const struct cc_enclave_ops g_ops = {
|
||||
.cc_create_enclave = _gp_create,
|
||||
.cc_destroy_enclave = _gp_destroy,
|
||||
.cc_ecall_enclave = cc_enclave_call_function,
|
||||
.cc_sl_ecall_enclave = cc_sl_enclave_call_function,
|
||||
+ .cc_sl_async_ecall = cc_sl_async_ecall,
|
||||
+ .cc_sl_async_ecall_get_result = cc_sl_async_ecall_check_result,
|
||||
.cc_malloc_shared_memory = gp_malloc_shared_memory,
|
||||
.cc_free_shared_memory = gp_free_shared_memory,
|
||||
.cc_register_shared_memory = gp_register_shared_memory,
|
||||
diff --git a/src/host_src/gp/gp_uswitchless.c b/src/host_src/gp/gp_uswitchless.c
|
||||
index f1288c2..2a315ea 100644
|
||||
--- a/src/host_src/gp/gp_uswitchless.c
|
||||
+++ b/src/host_src/gp/gp_uswitchless.c
|
||||
@@ -97,6 +97,21 @@ bool uswitchless_is_valid_param_num(cc_enclave_t *enclave, uint32_t argc)
|
||||
return argc <= USWITCHLESS_TASK_POOL(enclave)->pool_cfg.num_max_params;
|
||||
}
|
||||
|
||||
+bool uswitchless_is_valid_task_index(cc_enclave_t *enclave, int task_index)
|
||||
+{
|
||||
+ sl_task_pool_t *pool = USWITCHLESS_TASK_POOL(enclave);
|
||||
+ int task_total = pool->pool_cfg.sl_call_pool_size_qwords * SWITCHLESS_BITS_IN_QWORD;
|
||||
+
|
||||
+ if (task_index < 0 || task_index >= task_total) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ int i = task_index / SWITCHLESS_BITS_IN_QWORD;
|
||||
+ int j = task_index % SWITCHLESS_BITS_IN_QWORD;
|
||||
+
|
||||
+ return !((*(pool->free_bit_buf + i)) & (1UL << j));
|
||||
+}
|
||||
+
|
||||
int uswitchless_get_idle_task_index(cc_enclave_t *enclave)
|
||||
{
|
||||
sl_task_pool_t *pool = USWITCHLESS_TASK_POOL(enclave);
|
||||
@@ -144,11 +159,13 @@ static inline sl_task_t *uswitchless_get_task_by_index(cc_enclave_t *enclave, in
|
||||
return (sl_task_t *)(pool->task_buf + task_index * pool->per_task_size);
|
||||
}
|
||||
|
||||
-void uswitchless_fill_task(cc_enclave_t *enclave, int task_index, uint32_t func_id, uint32_t argc, const void *args)
|
||||
+void uswitchless_fill_task(cc_enclave_t *enclave, int task_index, uint16_t func_id, uint16_t retval_size,
|
||||
+ uint32_t argc, const void *args)
|
||||
{
|
||||
sl_task_t *task = uswitchless_get_task_by_index(enclave, task_index);
|
||||
|
||||
task->func_id = func_id;
|
||||
+ task->retval_size = retval_size;
|
||||
__atomic_store_n(&task->status, SL_TASK_INIT, __ATOMIC_RELEASE);
|
||||
memcpy(&task->params[0], args, sizeof(uint64_t) * argc);
|
||||
}
|
||||
@@ -165,7 +182,7 @@ void uswitchless_submit_task(cc_enclave_t *enclave, int task_index)
|
||||
|
||||
#define CA_TIMEOUT_IN_SEC 60
|
||||
#define CA_GETTIME_PER_CNT 100000000
|
||||
-cc_enclave_result_t uswitchless_get_task_result(cc_enclave_t *enclave, int task_index, void *retval, size_t retval_size)
|
||||
+cc_enclave_result_t uswitchless_get_task_result(cc_enclave_t *enclave, int task_index, void *retval)
|
||||
{
|
||||
sl_task_t *task = uswitchless_get_task_by_index(enclave, task_index);
|
||||
uint32_t cur_status;
|
||||
@@ -178,8 +195,8 @@ cc_enclave_result_t uswitchless_get_task_result(cc_enclave_t *enclave, int task_
|
||||
while (true) {
|
||||
cur_status = __atomic_load_n(&task->status, __ATOMIC_ACQUIRE);
|
||||
if (cur_status == SL_TASK_DONE_SUCCESS) {
|
||||
- if ((retval != NULL) && (retval_size != 0)) {
|
||||
- (void)memcpy(retval, (void *)&task->ret_val, retval_size);
|
||||
+ if ((retval != NULL) && (task->retval_size > 0)) {
|
||||
+ (void)memcpy(retval, (void *)&task->ret_val, task->retval_size);
|
||||
}
|
||||
|
||||
return CC_SUCCESS;
|
||||
@@ -199,3 +216,22 @@ cc_enclave_result_t uswitchless_get_task_result(cc_enclave_t *enclave, int task_
|
||||
|
||||
return CC_ERROR_TIMEOUT;
|
||||
}
|
||||
+
|
||||
+cc_enclave_result_t uswitchless_get_async_task_result(cc_enclave_t *enclave, int task_index, void *retval)
|
||||
+{
|
||||
+ sl_task_t *task = uswitchless_get_task_by_index(enclave, task_index);
|
||||
+ uint32_t cur_status;
|
||||
+
|
||||
+ cur_status = __atomic_load_n(&task->status, __ATOMIC_ACQUIRE);
|
||||
+ if (cur_status == SL_TASK_DONE_SUCCESS) {
|
||||
+ if ((retval != NULL) && (task->retval_size > 0)) {
|
||||
+ (void)memcpy(retval, (void *)&task->ret_val, task->retval_size);
|
||||
+ }
|
||||
+
|
||||
+ return CC_SUCCESS;
|
||||
+ } else if (cur_status == SL_TASK_DONE_FAILED) {
|
||||
+ return (cc_enclave_result_t)task->ret_val;
|
||||
+ }
|
||||
+
|
||||
+ return CC_ERROR_SWITCHLESS_ASYNC_TASK_UNFINISHED;
|
||||
+}
|
||||
diff --git a/src/host_src/gp/gp_uswitchless.h b/src/host_src/gp/gp_uswitchless.h
|
||||
index 4d957ce..13ac14a 100644
|
||||
--- a/src/host_src/gp/gp_uswitchless.h
|
||||
+++ b/src/host_src/gp/gp_uswitchless.h
|
||||
@@ -81,18 +81,26 @@ void uswitchless_put_idle_task_by_index(cc_enclave_t *enclave, int task_index);
|
||||
void uswitchless_submit_task(cc_enclave_t *enclave, int task_index);
|
||||
|
||||
/*
|
||||
- * Summary: submitting a task
|
||||
+ * Summary: Obtains the result of the switchless invoking task
|
||||
* Parameters:
|
||||
* enclave: enclave
|
||||
* task_index: index of an task area
|
||||
* ret_val: address that accepts the return value
|
||||
- * ret_val_size: size of the return value
|
||||
* Return: CC_SUCCESS, success; others failed.
|
||||
*/
|
||||
-cc_enclave_result_t uswitchless_get_task_result(cc_enclave_t *enclave,
|
||||
- int task_index,
|
||||
- void *ret_val,
|
||||
- size_t ret_val_size);
|
||||
+cc_enclave_result_t uswitchless_get_task_result(cc_enclave_t *enclave, int task_index, void *ret_val);
|
||||
+
|
||||
+/*
|
||||
+ * Summary: Obtains the result of the switchless asynchronous invoking task
|
||||
+ * Parameters:
|
||||
+ * enclave: enclave
|
||||
+ * task_index: index of an task area
|
||||
+ * ret_val: address that accepts the return value
|
||||
+ * Return: CC_SUCCESS, success;
|
||||
+ CC_ERROR_SWITCHLESS_ASYNC_TASK_UNFINISHED, the asynchronous invoking task is not completed;
|
||||
+ others failed.
|
||||
+ */
|
||||
+cc_enclave_result_t uswitchless_get_async_task_result(cc_enclave_t *enclave, int task_index, void *retval);
|
||||
|
||||
/*
|
||||
* Summary: whether the switchless features is enabled
|
||||
@@ -115,17 +123,30 @@ bool uswitchless_is_switchless_enabled(cc_enclave_t *enclave);
|
||||
*/
|
||||
bool uswitchless_is_valid_param_num(cc_enclave_t *enclave, uint32_t argc);
|
||||
|
||||
+/*
|
||||
+ * Summary: whether the task index is valid
|
||||
+ * Parameters:
|
||||
+ * enclave: enclave
|
||||
+ * argc: task index
|
||||
+ * Return:
|
||||
+ * true: the task index is valid
|
||||
+ * false: invalid task index
|
||||
+ */
|
||||
+bool uswitchless_is_valid_task_index(cc_enclave_t *enclave, int task_index);
|
||||
+
|
||||
/*
|
||||
* Summary: fill a task
|
||||
* Parameters:
|
||||
* enclave: enclave
|
||||
* task_index: index of an task area
|
||||
* func_id: switchless function index
|
||||
+ * retval_size: size of the return value of the function
|
||||
* argc: number of parameters
|
||||
* args: parameter buffer
|
||||
* Return: NA
|
||||
*/
|
||||
-void uswitchless_fill_task(cc_enclave_t *enclave, int task_index, uint32_t func_id, uint32_t argc, const void *args);
|
||||
+void uswitchless_fill_task(cc_enclave_t *enclave, int task_index, uint16_t func_id, uint16_t retval_size,
|
||||
+ uint32_t argc, const void *args);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
diff --git a/tools/codegener/Genheader.ml b/tools/codegener/Genheader.ml
|
||||
index 9d0514c..e00157d 100644
|
||||
--- a/tools/codegener/Genheader.ml
|
||||
+++ b/tools/codegener/Genheader.ml
|
||||
@@ -71,6 +71,27 @@ let generate_rproxy_prototype (fd: func_decl) =
|
||||
"cc_enclave_result_t " ^ func_name ^ enclave_decl ^ func_args ^")";
|
||||
]
|
||||
|
||||
+let generate_rproxy_prototype_sl_async (tf: trusted_func) =
|
||||
+ if not tf.tf_is_switchless then
|
||||
+ [""]
|
||||
+ else
|
||||
+ let fd = tf.tf_fdecl in
|
||||
+ let func_name = fd.fname ^ "_async" in
|
||||
+ let enclave_decl = "(\n cc_enclave_t *enclave,\n int *task_id" in
|
||||
+ let func_args =
|
||||
+ let func_args_list =
|
||||
+ List.map (fun f -> gen_parm_str f) fd.plist
|
||||
+ in
|
||||
+ if List.length fd.plist > 0 then
|
||||
+ let func_args_pre = String.concat ",\n " func_args_list in
|
||||
+ ",\n " ^ func_args_pre
|
||||
+ else
|
||||
+ ""
|
||||
+ in
|
||||
+ [
|
||||
+ "cc_enclave_result_t " ^ func_name ^ enclave_decl ^ func_args ^")";
|
||||
+ ]
|
||||
+
|
||||
let generate_parm_str (p: pdecl) =
|
||||
let (_, declr) = p in
|
||||
declr.identifier
|
||||
@@ -344,14 +365,20 @@ let generate_untrusted_header (ec: enclave_content) =
|
||||
let r_proxy_proto =
|
||||
List.map (fun f -> generate_rproxy_prototype f.tf_fdecl) ec.tfunc_decls
|
||||
in
|
||||
+ let r_proxy_proto_sl_async =
|
||||
+ List.map (fun f -> generate_rproxy_prototype_sl_async f) ec.tfunc_decls
|
||||
+ in
|
||||
let r_proxy =
|
||||
String.concat ";\n\n" (List.flatten r_proxy_proto)
|
||||
in
|
||||
+ let r_proxy_sl_async =
|
||||
+ String.concat ";\n\n" (List.flatten r_proxy_proto_sl_async)
|
||||
+ in
|
||||
[
|
||||
hfile_start ^ hfile_include;
|
||||
c_start;
|
||||
agent_id;
|
||||
- trust_fproto_com ^ r_proxy ^ ";";
|
||||
+ trust_fproto_com ^ r_proxy ^ r_proxy_sl_async ^ ";";
|
||||
if (List.length ec.ufunc_decls <> 0) then untrust_fproto_com ^ untrust_func ^ ";"
|
||||
else "/**** There is no untrusted function ****/";
|
||||
c_end;
|
||||
diff --git a/tools/codegener/Genuntrust.ml b/tools/codegener/Genuntrust.ml
|
||||
index dc3010d..6fb4967 100644
|
||||
--- a/tools/codegener/Genuntrust.ml
|
||||
+++ b/tools/codegener/Genuntrust.ml
|
||||
@@ -98,6 +98,24 @@ let set_ecall_func_arguments (fd : func_decl) =
|
||||
else "")
|
||||
]
|
||||
|
||||
+let set_sl_async_ecall_func_arguments (fd : func_decl) =
|
||||
+ [
|
||||
+ sprintf "cc_enclave_result_t %s(\n %s" (fd.fname ^ "_async") "cc_enclave_t *enclave,\n int *task_id"
|
||||
+ ^ (if fd.plist <> [] then
|
||||
+ ",\n " ^
|
||||
+ concat ",\n "
|
||||
+ (List.map
|
||||
+ (fun (ptype, decl) ->
|
||||
+ match ptype with
|
||||
+ PTVal ty -> (sprintf "%s %s" (get_tystr ty) decl.identifier)
|
||||
+ | PTPtr (t, a) -> match (a.pa_rdonly, is_array decl) with
|
||||
+ | (true, false) -> sprintf "const %s %s" (get_tystr t) decl.identifier
|
||||
+ | (false, true) -> sprintf "%s %s%s" (get_tystr t) decl.identifier (set_array_dims_str decl.array_dims)
|
||||
+ | (_, _) -> sprintf "%s %s" (get_tystr t) decl.identifier)
|
||||
+ fd.plist)
|
||||
+ else "")
|
||||
+ ]
|
||||
+
|
||||
let set_sl_ecall_func (tf : trusted_func) =
|
||||
let tfd = tf.tf_fdecl in
|
||||
let init_point = set_init_pointer tfd in
|
||||
@@ -162,20 +180,52 @@ let set_sl_ecall_func (tf : trusted_func) =
|
||||
" /* Call the cc_enclave function */";
|
||||
|
||||
" sl_ecall_func_info_t func_info = {";
|
||||
- " .func_id = " ^ "fid_" ^ tfd.fname ^ ",";
|
||||
+ " .func_id = " ^ "fid_" ^ tfd.fname ^ ",\n .retval_size= " ^ out_retval_size ^ ",";
|
||||
" .argc = " ^ num_params ^ ",";
|
||||
" .args = " ^ out_params ^ ",";
|
||||
" };";
|
||||
|
||||
" ret = enclave->list_ops_node->ops_desc->ops->cc_sl_ecall_enclave(enclave,";
|
||||
" " ^ out_retval ^ ",";
|
||||
- " " ^ out_retval_size ^ ",";
|
||||
" &func_info);\n";
|
||||
|
||||
" pthread_rwlock_unlock(&enclave->rwlock);";
|
||||
(if tfd.plist <> [] then " free(params_buf);" else "");
|
||||
" return ret;";
|
||||
"}";
|
||||
+
|
||||
+ "";
|
||||
+ concat ",\n " (set_sl_async_ecall_func_arguments tfd) ^ ")";
|
||||
+ "{";
|
||||
+ " cc_enclave_result_t ret;\n";
|
||||
+ " if (enclave == NULL) {";
|
||||
+ " return CC_ERROR_BAD_PARAMETERS;";
|
||||
+ " }\n";
|
||||
+ " if (pthread_rwlock_rdlock(&enclave->rwlock)) {";
|
||||
+ " return CC_ERROR_BUSY;";
|
||||
+ " }\n";
|
||||
+ " if (enclave->list_ops_node == NULL ||\n enclave->list_ops_node->ops_desc == NULL ||";
|
||||
+ " enclave->list_ops_node->ops_desc->ops == NULL ||";
|
||||
+ " enclave->list_ops_node->ops_desc->ops->cc_sl_async_ecall == NULL) {";
|
||||
+ " pthread_rwlock_unlock(&enclave->rwlock);";
|
||||
+ " return CC_ERROR_BAD_PARAMETERS;";
|
||||
+ " }";
|
||||
+ "";
|
||||
+ params;
|
||||
+ " /* Call the cc_enclave function */";
|
||||
+
|
||||
+ " sl_ecall_func_info_t func_info = {";
|
||||
+ " .func_id = " ^ "fid_" ^ tfd.fname ^ ",\n .retval_size= " ^ out_retval_size ^ ",";
|
||||
+ " .argc = " ^ num_params ^ ",";
|
||||
+ " .args = " ^ out_params ^ ",";
|
||||
+ " };";
|
||||
+
|
||||
+ " ret = enclave->list_ops_node->ops_desc->ops->cc_sl_async_ecall(enclave, task_id, &func_info);\n";
|
||||
+
|
||||
+ " pthread_rwlock_unlock(&enclave->rwlock);";
|
||||
+ (if tfd.plist <> [] then " free(params_buf);" else "");
|
||||
+ " return ret;";
|
||||
+ "}";
|
||||
]
|
||||
|
||||
let set_ecall_func (tf : trusted_func) =
|
||||
--
|
||||
2.27.0
|
||||
|
||||
307
0052-rollback-to-common-invoking-when-async-invoking-fail.patch
Normal file
307
0052-rollback-to-common-invoking-when-async-invoking-fail.patch
Normal file
@ -0,0 +1,307 @@
|
||||
From 232ba565206caf01e7f514c0c5735a8e8d3ae06a Mon Sep 17 00:00:00 2001
|
||||
From: modric <wangyu283@huawei.com>
|
||||
Date: Wed, 9 Nov 2022 15:17:28 +0800
|
||||
Subject: [PATCH 3/4] rollback to common invoking when async invoking fails
|
||||
|
||||
---
|
||||
inc/host_inc/secgear_uswitchless.h | 7 ++-
|
||||
inc/host_inc/status.h | 1 +
|
||||
src/host_src/gp/gp_enclave.c | 5 ++
|
||||
src/host_src/gp/gp_uswitchless.c | 5 ++
|
||||
src/host_src/gp/gp_uswitchless.h | 10 ++++
|
||||
tools/codegener/Genheader.ml | 13 ++++-
|
||||
tools/codegener/Gentrust.ml | 10 ++--
|
||||
tools/codegener/Genuntrust.ml | 92 +++++++++++++++++++++++++++++-
|
||||
8 files changed, 133 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/inc/host_inc/secgear_uswitchless.h b/inc/host_inc/secgear_uswitchless.h
|
||||
index 8e21fd9..2ea4691 100644
|
||||
--- a/inc/host_inc/secgear_uswitchless.h
|
||||
+++ b/inc/host_inc/secgear_uswitchless.h
|
||||
@@ -81,10 +81,13 @@ typedef struct {
|
||||
uint32_t retries_before_sleep;
|
||||
|
||||
/* Worker thread scheduling policy, refer to cc_workers_policy_t, only for GP */
|
||||
- uint64_t workers_policy;
|
||||
+ uint32_t workers_policy;
|
||||
+
|
||||
+ /* Indicates whether to roll back to common invoking when asynchronous switchless invoking fails, only for GP */
|
||||
+ uint32_t rollback_to_common;
|
||||
} cc_sl_config_t;
|
||||
|
||||
-#define CC_USWITCHLESS_CONFIG_INITIALIZER {1, 1, 1, 16, 0, 0, WORKERS_POLICY_BUSY}
|
||||
+#define CC_USWITCHLESS_CONFIG_INITIALIZER {1, 1, 1, 16, 0, 0, WORKERS_POLICY_BUSY, 0}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
diff --git a/inc/host_inc/status.h b/inc/host_inc/status.h
|
||||
index 4f982f8..7f8daaa 100644
|
||||
--- a/inc/host_inc/status.h
|
||||
+++ b/inc/host_inc/status.h
|
||||
@@ -168,6 +168,7 @@ typedef enum _enclave_result_t
|
||||
CC_ERROR_ADDRESS_UNACCESSABLE, /* Memory address is not within enclave */
|
||||
CC_ERROR_SWITCHLESS_INVALID_TASK_ID, /* Invalid invoking task ID */
|
||||
CC_ERROR_SWITCHLESS_ASYNC_TASK_UNFINISHED, /* The asynchronous invoking task is not completed */
|
||||
+ CC_ERROR_SWITCHLESS_ROLLBACK2COMMON, /* rollback to common invoking when async invoking fails */
|
||||
CC_MAXIMUM_ERROR,
|
||||
} cc_enclave_result_t;
|
||||
|
||||
diff --git a/src/host_src/gp/gp_enclave.c b/src/host_src/gp/gp_enclave.c
|
||||
index 5345973..521a850 100644
|
||||
--- a/src/host_src/gp/gp_enclave.c
|
||||
+++ b/src/host_src/gp/gp_enclave.c
|
||||
@@ -857,6 +857,11 @@ cc_enclave_result_t cc_sl_async_ecall(cc_enclave_t *enclave, int *task_id, sl_ec
|
||||
|
||||
int task_index = uswitchless_get_idle_task_index(enclave);
|
||||
if (task_index < 0) {
|
||||
+ /* Need roll back to common invoking when asynchronous invoking fails. */
|
||||
+ if (uswitchless_need_rollback_to_common(enclave)) {
|
||||
+ return CC_ERROR_SWITCHLESS_ROLLBACK2COMMON;
|
||||
+ }
|
||||
+
|
||||
return CC_ERROR_SWITCHLESS_TASK_POOL_FULL;
|
||||
}
|
||||
|
||||
diff --git a/src/host_src/gp/gp_uswitchless.c b/src/host_src/gp/gp_uswitchless.c
|
||||
index 2a315ea..53ecc55 100644
|
||||
--- a/src/host_src/gp/gp_uswitchless.c
|
||||
+++ b/src/host_src/gp/gp_uswitchless.c
|
||||
@@ -112,6 +112,11 @@ bool uswitchless_is_valid_task_index(cc_enclave_t *enclave, int task_index)
|
||||
return !((*(pool->free_bit_buf + i)) & (1UL << j));
|
||||
}
|
||||
|
||||
+bool uswitchless_need_rollback_to_common(cc_enclave_t *enclave)
|
||||
+{
|
||||
+ return USWITCHLESS_TASK_POOL(enclave)->pool_cfg.rollback_to_common > 0;
|
||||
+}
|
||||
+
|
||||
int uswitchless_get_idle_task_index(cc_enclave_t *enclave)
|
||||
{
|
||||
sl_task_pool_t *pool = USWITCHLESS_TASK_POOL(enclave);
|
||||
diff --git a/src/host_src/gp/gp_uswitchless.h b/src/host_src/gp/gp_uswitchless.h
|
||||
index 13ac14a..a0ea117 100644
|
||||
--- a/src/host_src/gp/gp_uswitchless.h
|
||||
+++ b/src/host_src/gp/gp_uswitchless.h
|
||||
@@ -134,6 +134,16 @@ bool uswitchless_is_valid_param_num(cc_enclave_t *enclave, uint32_t argc);
|
||||
*/
|
||||
bool uswitchless_is_valid_task_index(cc_enclave_t *enclave, int task_index);
|
||||
|
||||
+/*
|
||||
+ * Summary: whether to roll back to common invoking when asynchronous switchless invoking fails
|
||||
+ * Parameters:
|
||||
+ * enclave: enclave
|
||||
+ * Return:
|
||||
+ * true: yes
|
||||
+ * false: no
|
||||
+ */
|
||||
+bool uswitchless_need_rollback_to_common(cc_enclave_t *enclave);
|
||||
+
|
||||
/*
|
||||
* Summary: fill a task
|
||||
* Parameters:
|
||||
diff --git a/tools/codegener/Genheader.ml b/tools/codegener/Genheader.ml
|
||||
index e00157d..0f244f3 100644
|
||||
--- a/tools/codegener/Genheader.ml
|
||||
+++ b/tools/codegener/Genheader.ml
|
||||
@@ -31,6 +31,14 @@ let generate_args_include (ufs: untrusted_func list) =
|
||||
"#include \"enclave.h\"\n" ^
|
||||
error_include ^ "\n"
|
||||
|
||||
+let generate_function_id_ex (tf: trusted_func) =
|
||||
+ let f = tf.tf_fdecl in
|
||||
+ let f_name = f.fname in
|
||||
+ if tf.tf_is_switchless then
|
||||
+ "fid_sl_async_" ^ f_name
|
||||
+ else
|
||||
+ "fid_" ^ f_name
|
||||
+
|
||||
let generate_function_id (f: func_decl) =
|
||||
let f_name = f.fname in
|
||||
"fid_" ^ f_name
|
||||
@@ -77,7 +85,8 @@ let generate_rproxy_prototype_sl_async (tf: trusted_func) =
|
||||
else
|
||||
let fd = tf.tf_fdecl in
|
||||
let func_name = fd.fname ^ "_async" in
|
||||
- let enclave_decl = "(\n cc_enclave_t *enclave,\n int *task_id" in
|
||||
+ let enclave_decl =
|
||||
+ "(\n " ^ (match fd.rtype with Void -> "cc_enclave_t *enclave,\n int *task_id" | _ -> "cc_enclave_t *enclave,\n int *task_id,\n " ^ (get_tystr fd.rtype ^ " *retval")) in
|
||||
let func_args =
|
||||
let func_args_list =
|
||||
List.map (fun f -> gen_parm_str f) fd.plist
|
||||
@@ -270,7 +279,7 @@ let generate_args_header (ec: enclave_content) =
|
||||
let trust_fid_body =
|
||||
let trust_fid_pre =
|
||||
List.mapi
|
||||
- (fun i f -> sprintf " %s = %d," (generate_function_id f.tf_fdecl) (i + 2)) tfunc_decls
|
||||
+ (fun i f -> sprintf " %s = %d," (generate_function_id_ex f) (i + 2)) ec.tfunc_decls
|
||||
in
|
||||
String.concat "\n" trust_fid_pre
|
||||
in
|
||||
diff --git a/tools/codegener/Gentrust.ml b/tools/codegener/Gentrust.ml
|
||||
index 6b6fa00..d950899 100644
|
||||
--- a/tools/codegener/Gentrust.ml
|
||||
+++ b/tools/codegener/Gentrust.ml
|
||||
@@ -146,6 +146,7 @@ let set_switchless_ecall_func (tf : trusted_func) =
|
||||
match tfd.rtype with
|
||||
| Void -> ""
|
||||
| _ -> " (void)memcpy(retval, &ret, sizeof(ret));" in
|
||||
+ if tf.tf_is_switchless then
|
||||
[
|
||||
sprintf "\nvoid sl_ecall_%s(void *task_buf)" tfd.fname;
|
||||
"{";
|
||||
@@ -160,15 +161,15 @@ let set_switchless_ecall_func (tf : trusted_func) =
|
||||
write_back_retval;
|
||||
"}";
|
||||
]
|
||||
+ else ["";]
|
||||
|
||||
let set_ecall_func (tf : trusted_func) =
|
||||
- if tf.tf_is_switchless then
|
||||
- set_switchless_ecall_func tf
|
||||
- else
|
||||
+ let slfunc = String.concat " " (set_switchless_ecall_func tf) in
|
||||
let tfd = tf.tf_fdecl in
|
||||
let params_point = set_parameters_point tfd in
|
||||
let out_params = set_out_params tfd in
|
||||
[
|
||||
+ "" ^ slfunc;
|
||||
sprintf "cc_enclave_result_t ecall_%s (" tfd.fname;
|
||||
" uint8_t* in_buf,";
|
||||
" size_t in_buf_size,";
|
||||
@@ -396,8 +397,7 @@ let gen_trusted(ec : enclave_content) =
|
||||
" (cc_ecall_func_t) ecall_unregister_shared_memory,";
|
||||
" " ^ concat ",\n "
|
||||
(List.map (fun (tf) ->
|
||||
- sprintf "(cc_ecall_func_t) ecall_%s" tf.tf_fdecl.fname)
|
||||
- (List.filter (fun tf -> not tf.tf_is_switchless) trust_funcs));
|
||||
+ sprintf "(cc_ecall_func_t) ecall_%s" tf.tf_fdecl.fname) trust_funcs);
|
||||
"};";
|
||||
"";
|
||||
"size_t ecall_table_size = CC_ARRAY_LEN(cc_ecall_tables);\n";
|
||||
diff --git a/tools/codegener/Genuntrust.ml b/tools/codegener/Genuntrust.ml
|
||||
index 6fb4967..8bc8e03 100644
|
||||
--- a/tools/codegener/Genuntrust.ml
|
||||
+++ b/tools/codegener/Genuntrust.ml
|
||||
@@ -80,6 +80,40 @@ let set_call_user_func (fd : func_decl) =
|
||||
"}";
|
||||
]
|
||||
|
||||
+let sl_async_set_call_user_func (fd : func_decl) =
|
||||
+ [
|
||||
+ "/* Call the cc_enclave function */";
|
||||
+ "if (!enclave) {";
|
||||
+ " ret = CC_ERROR_BAD_PARAMETERS;";
|
||||
+ " goto exit;";
|
||||
+ "}";
|
||||
+ "if (pthread_rwlock_rdlock(&enclave->rwlock)) {";
|
||||
+ " ret = CC_ERROR_BUSY;";
|
||||
+ " goto exit;";
|
||||
+ "}";
|
||||
+ "if (!enclave->list_ops_node || !enclave->list_ops_node->ops_desc ||";
|
||||
+ " !enclave->list_ops_node->ops_desc->ops ||";
|
||||
+ " !enclave->list_ops_node->ops_desc->ops->cc_ecall_enclave) {";
|
||||
+ " ret = CC_ERROR_BAD_PARAMETERS;";
|
||||
+ " goto exit;";
|
||||
+ "}";
|
||||
+ "if ((ret = enclave->list_ops_node->ops_desc->ops->cc_ecall_enclave(";
|
||||
+ " enclave,";
|
||||
+ sprintf " fid_sl_async_%s," fd.fname;
|
||||
+ " in_buf,";
|
||||
+ " in_buf_size,";
|
||||
+ " out_buf,";
|
||||
+ " out_buf_size,";
|
||||
+ " &ms,";
|
||||
+ " &ocall_table)) != CC_SUCCESS) {";
|
||||
+ " pthread_rwlock_unlock(&enclave->rwlock);";
|
||||
+ " goto exit; }";
|
||||
+ "if (pthread_rwlock_unlock(&enclave->rwlock)) {";
|
||||
+ " ret = CC_ERROR_BUSY;";
|
||||
+ " goto exit;";
|
||||
+ "}";
|
||||
+ ]
|
||||
+
|
||||
let set_ecall_func_arguments (fd : func_decl) =
|
||||
[
|
||||
sprintf "cc_enclave_result_t %s(\n %s" fd.fname (match fd.rtype with Void -> "cc_enclave_t *enclave" | _ -> "cc_enclave_t *enclave,\n " ^ (get_tystr fd.rtype ^ "* retval"))
|
||||
@@ -100,7 +134,7 @@ let set_ecall_func_arguments (fd : func_decl) =
|
||||
|
||||
let set_sl_async_ecall_func_arguments (fd : func_decl) =
|
||||
[
|
||||
- sprintf "cc_enclave_result_t %s(\n %s" (fd.fname ^ "_async") "cc_enclave_t *enclave,\n int *task_id"
|
||||
+ sprintf "cc_enclave_result_t %s(\n %s" (fd.fname ^ "_async") (match fd.rtype with Void -> "cc_enclave_t *enclave,\n int *task_id" | _ -> "cc_enclave_t *enclave,\n int *task_id,\n " ^ (get_tystr fd.rtype ^ " *retval"))
|
||||
^ (if fd.plist <> [] then
|
||||
",\n " ^
|
||||
concat ",\n "
|
||||
@@ -119,6 +153,7 @@ let set_sl_async_ecall_func_arguments (fd : func_decl) =
|
||||
let set_sl_ecall_func (tf : trusted_func) =
|
||||
let tfd = tf.tf_fdecl in
|
||||
let init_point = set_init_pointer tfd in
|
||||
+ let arg_size = set_args_size tfd in
|
||||
let get_param_name (_, decl) = decl.identifier in
|
||||
(*let is_ptr_type (ptype) =
|
||||
match ptype with
|
||||
@@ -224,6 +259,61 @@ let set_sl_ecall_func (tf : trusted_func) =
|
||||
|
||||
" pthread_rwlock_unlock(&enclave->rwlock);";
|
||||
(if tfd.plist <> [] then " free(params_buf);" else "");
|
||||
+ " if (ret != CC_ERROR_SWITCHLESS_ROLLBACK2COMMON) {\n return ret;\n }";
|
||||
+ "\n /* rollback to common invoking when async invoking fails */";
|
||||
+ " ret = CC_FAIL;";
|
||||
+ " *task_id = -1;";
|
||||
+ "";
|
||||
+ " /* Init buffer and size */";
|
||||
+ " size_t in_buf_size = 0;";
|
||||
+ " size_t out_buf_size = 0;";
|
||||
+ " uint8_t* in_buf = NULL;";
|
||||
+ " uint8_t* out_buf = NULL;";
|
||||
+ " uint32_t ms = TEE_SECE_AGENT_ID;";
|
||||
+ sprintf " %s_size_t args_size;" tfd.fname;
|
||||
+ "";
|
||||
+ " /* Init pointer */";
|
||||
+ if init_point <> ["";"";""] then
|
||||
+ concat "\n" init_point
|
||||
+ else " /* There is no pointer */";
|
||||
+ "";
|
||||
+ " memset(&args_size, 0, sizeof(args_size));";
|
||||
+ " /* Fill argments size */";
|
||||
+ if arg_size <> [""] then
|
||||
+ " " ^ concat "\n " (set_args_size tfd)
|
||||
+ else "/* There is no argments size */";
|
||||
+ "";
|
||||
+ sprintf " in_buf_size += size_to_aligned_size(sizeof(%s_size_t));"
|
||||
+ tfd.fname;
|
||||
+
|
||||
+ " " ^ concat "\n " (set_data_in tfd);
|
||||
+ "";
|
||||
+
|
||||
+ " " ^ concat "\n " (set_data_out tfd);
|
||||
+ "";
|
||||
+ " /* Allocate in_buf and out_buf */";
|
||||
+ " in_buf = (uint8_t*)malloc(in_buf_size);";
|
||||
+ " out_buf = (uint8_t*)malloc(out_buf_size);";
|
||||
+ " if (in_buf == NULL || out_buf == NULL) {";
|
||||
+ " ret = CC_ERROR_OUT_OF_MEMORY;";
|
||||
+ " goto exit;";
|
||||
+ " }";
|
||||
+
|
||||
+ "";
|
||||
+ " " ^ concat "\n " (set_in_memcpy tfd);
|
||||
+ "";
|
||||
+ " " ^ concat "\n " (sl_async_set_call_user_func tfd);
|
||||
+ "";
|
||||
+ " " ^ concat "\n " (set_out_memcpy tfd);
|
||||
+ " ret = CC_SUCCESS;";
|
||||
+ "";
|
||||
+
|
||||
+ "exit:";
|
||||
+ " if (in_buf)";
|
||||
+ " free(in_buf);";
|
||||
+ " if (out_buf)";
|
||||
+ " free(out_buf);";
|
||||
+ "";
|
||||
" return ret;";
|
||||
"}";
|
||||
]
|
||||
--
|
||||
2.27.0
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
Name: secGear
|
||||
Version: 0.1.0
|
||||
Release: 32
|
||||
Release: 33
|
||||
Summary: secGear is an SDK to develop confidential computing apps based on hardware enclave features
|
||||
|
||||
|
||||
@ -59,6 +59,8 @@ Patch46: 0047-del-print-uncontrol-form-string.patch
|
||||
Patch47: 0048-Delete-the-null-determination-of-out_buf-in-codegene.patch
|
||||
Patch48: 0049-support-switchless-feature.patch
|
||||
Patch49: 0050-switchless-schedule-policy.patch
|
||||
Patch50: 0051-asynchronous-switchless.patch
|
||||
Patch51: 0052-rollback-to-common-invoking-when-async-invoking-fail.patch
|
||||
|
||||
BuildRequires: gcc python automake autoconf libtool
|
||||
BUildRequires: glibc glibc-devel cmake ocaml-dune rpm gcc-c++
|
||||
@ -177,6 +179,9 @@ popd
|
||||
systemctl restart rsyslog
|
||||
|
||||
%changelog
|
||||
* Sat Dec 17 2022 houmingyong<houmingyong@huawei.com> - 0.1.0-33
|
||||
- switchless support asynchronous ecall
|
||||
|
||||
* Tue Nov 22 2022 houmingyong<houmingyong@huawei.com> - 0.1.0-32
|
||||
- switchless support configure schedule policy
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user