1 /*
2 * Copyright (C) 2022 Huawei Technologies Co., Ltd.
3 * Licensed under the Mulan PSL v2.
4 * You can use this software according to the terms and conditions of the Mulan PSL v2.
5 * You may obtain a copy of Mulan PSL v2 at:
6 * http://license.coscl.org.cn/MulanPSL2
7 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
8 * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
9 * PURPOSE.
10 * See the Mulan PSL v2 for more details.
11 */
12 #include "drv_process_mgr.h"
13 #include <stdint.h>
14 #include <securec.h>
15 #include <dlfcn.h>
16 #include <pthread.h>
17 #include <ipclib.h>
18 #include <spawn_ext.h>
19 #include <sys/wait.h>
20 #include <signal.h>
21 #include <fileio.h>
22 #include <priorities.h>
23 #include <tee_log.h>
24 #include <drv_thread.h>
25 #include <spawn_init.h>
26 #include <get_elf_info.h>
27 #include <target_type.h>
28 #include <tee_drv_internal.h>
29 #include <mem_ops.h>
30 #include <mem_page_ops.h>
31 #include "drv_fd_ops.h"
32 #include "drv_dyn_policy_mgr.h"
33 #include "task_mgr.h"
34 #include "base_drv_node.h"
35 #include <ipclib_hal.h>
36
37 static const char *g_drv_loader = "/tarunner.elf";
38 static const char *g_drv_a32_loader = "/tarunner_a32.elf";
39
40 /*
41 * channel and msghdl used for send/receive ipc msg
42 * between drvmgr and drv process, locked by g_drv_spawn_mtx
43 */
44 static cref_t g_drv_spawn_sync_channel;
45 static cref_t g_drv_spawn_sync_msghdl;
46
create_spawn_sync_msg_info(void)47 int32_t create_spawn_sync_msg_info(void)
48 {
49 int32_t ret = ipc_create_channel_native(DRV_SPAWN_SYNC_NAME, &g_drv_spawn_sync_channel);
50 if (ret != 0) {
51 /* called by drvmgr main, use tloge instead of tloge */
52 tloge("create spawn sync channel fail\n");
53 return -1;
54 }
55
56 g_drv_spawn_sync_msghdl = ipc_msg_create_hdl();
57 if (!check_ref_valid(g_drv_spawn_sync_msghdl)) {
58 tloge("create spawn sync hdl fail\n");
59 return -1;
60 }
61
62 return 0;
63 }
64
65 /*
66 * process argv and env which will be passed to driver loader by kernel,
67 * all this buffer should be in drvmgr map pages, otherwise will cannot
68 * find map_page in find_map_page called by vspace_stream_sync.
69 * Now, only the stack of main thread or the global value added to map pages,
70 * so in order to spawn in other thread, we use global value as the spawn buffer.
71 */
72 static char g_spawn_buffer[sizeof(struct spawn_drv_buffer) * 2];
73 static char *g_argv[ARGV_MAX] = { 0 };
74 static char *g_env[ENV_MAX] = { 0 };
75 static pthread_mutex_t g_drv_spawn_mtx = PTHREAD_MUTEX_INITIALIZER;
76
spawn_driver(const struct drv_spawn_param * param,int32_t loader_type,char * argv[],char * env[],uint32_t * taskid)77 static int32_t spawn_driver(const struct drv_spawn_param *param, int32_t loader_type,
78 char *argv[], char *env[], uint32_t *taskid)
79 {
80 pid_t pid;
81 tid_t tid;
82 posix_spawnattr_t spawnattr;
83
84 int32_t ret = spawnattr_init(&spawnattr);
85 if (ret != 0) {
86 tloge("spawnattr init failed\n");
87 return -1;
88 }
89
90 ret = spawnattr_setstack(&spawnattr, param->stack_size);
91 if (ret != 0) {
92 tloge("set stack size:0x%x failed\n", param->stack_size);
93 return -1;
94 }
95
96 ret = spawnattr_setheap(&spawnattr, param->heap_size);
97 if (ret != 0) {
98 tloge("set heap size:0x%x failed\n", param->heap_size);
99 return -1;
100 }
101
102 spawn_uuid_t uuid;
103 uuid.uuid_valid = 0; /* drvloader in ramfs, not tafs */
104 if (memcpy_s(&uuid.uuid, sizeof(uuid.uuid), ¶m->uuid, sizeof(param->uuid)) != 0) {
105 tloge("set uuid failed\n");
106 return -1;
107 }
108 spawnattr_setuuid(&spawnattr, &uuid);
109
110 spawnattr.ptid = 0;
111
112 const char *drv_loader = g_drv_loader;
113 if (loader_type == ELF_TARUNNER_A32)
114 drv_loader = g_drv_a32_loader;
115
116 ret = posix_spawn_ex(&pid, drv_loader, NULL, &spawnattr, argv, env, &tid);
117 if (ret != 0) {
118 tloge("spawn driver failed ret:0x%x\n", ret);
119 return -1;
120 }
121
122 *taskid = pid_to_taskid((uint32_t)tid, (uint32_t)pid);
123
124 return 0;
125 }
126
drv_name_to_path(const struct drv_spawn_param * drv_param,char * drv_path,int32_t path_len)127 static int32_t drv_name_to_path(const struct drv_spawn_param *drv_param, char *drv_path, int32_t path_len)
128 {
129 bool base_drv_flag = get_base_drv_flag(drv_param->drv_name, strlen(drv_param->drv_name));
130 if (base_drv_flag) {
131 int32_t ret = snprintf_s(drv_path, path_len, (path_len - 1), "/%s%s", drv_param->drv_name, ".elf");
132 if (ret < 0) {
133 tloge("get drv path failed\n");
134 return -1;
135 }
136 } else {
137 int32_t ret = snprintf_s(drv_path, path_len, (path_len - 1), "%s/%s%s",
138 TAFS_MOUNTPOINT, drv_param->drv_name, ".elf");
139 if (ret < 0) {
140 tloge("get drv path failed\n");
141 return -1;
142 }
143 }
144
145 tlogd("get drv path is %s\n", drv_path);
146
147 return 0;
148 }
149
init_spawn_argv(const char * drv_name,uint32_t drv_name_len,const char * path_name,uint32_t path_len,struct argv_base_buffer * argv)150 static int32_t init_spawn_argv(const char *drv_name, uint32_t drv_name_len,
151 const char *path_name, uint32_t path_len, struct argv_base_buffer *argv)
152 {
153 /* uncommit default is true */
154 if (strncpy_s(argv->task_name, sizeof(argv->task_name), drv_name, drv_name_len) != 0) {
155 tloge("set loader path failed\n");
156 return -1;
157 }
158
159 if (strncpy_s(argv->task_path, sizeof(argv->task_path), path_name, path_len) != 0) {
160 tloge("set task path name failed\n");
161 return -1;
162 }
163
164 return 0;
165 }
166
init_spawn_buffer(struct spawn_drv_buffer * buffer)167 static void init_spawn_buffer(struct spawn_drv_buffer *buffer)
168 {
169 g_argv[ARGV_ELF_PATH_INDEX] = (char *)g_drv_loader;
170 g_argv[ARGV_TASK_NAME_INDEX] = buffer->argv.task_name;
171 g_argv[ARGV_TASK_PATH_INDEX] = buffer->argv.task_path;
172 g_argv[ARGV_UNCOMMIT_INDEX] = buffer->argv.uncommit;
173
174 g_env[ENV_PRIORITY_INDEX] = buffer->env.priority;
175 g_env[ENV_UID_INDEX] = buffer->env.uid;
176 g_env[ENV_TARGET_TYPE_INDEX] = buffer->env.target_type;
177 g_env[ENV_DRV_INDEX_INDEX] = buffer->env_drv.drv_index;
178 g_env[ENV_THREAD_LIMIT_INDEX] = buffer->env_drv.thread_limit;
179 g_env[ENV_STACK_SIZE_INDEX] = buffer->env_drv.stack_size;
180 }
181
init_spawn_env(const struct drv_spawn_param * drv_param,struct spawn_drv_buffer * buffer)182 static int32_t init_spawn_env(const struct drv_spawn_param *drv_param, struct spawn_drv_buffer *buffer)
183 {
184 struct env_param eparam = { 0 };
185 eparam.priority = PRIO_TEE_DRV;
186 eparam.target_type = DRV_TARGET_TYPE;
187 eparam.drv_index = drv_param->drv_index;
188 eparam.thread_limit = drv_param->thread_limit;
189 eparam.stack_size = drv_param->stack_size;
190
191 int32_t ret = set_env_for_task(&eparam, &(drv_param->uuid), &(buffer->env));
192 if (ret != 0)
193 return -1;
194
195 ret = set_drv_env_for_task(&eparam, &(buffer->env_drv));
196 if (ret != 0)
197 return -1;
198
199 return 0;
200 }
201
get_drv_loader(const char * name)202 static int32_t get_drv_loader(const char *name)
203 {
204 char ehdr[EH_SIZE];
205 int32_t loader = ELF_NOT_SUPPORT;
206
207 int32_t fd = open(name, O_RDONLY);
208 if (fd < 0) {
209 tloge("cannot open file %d\n", fd);
210 return loader;
211 }
212
213 if (read(fd, ehdr, sizeof(ehdr)) != sizeof(ehdr)) {
214 tloge("read file failed, name=%s\n", name);
215 goto close_fd;
216 }
217
218 int32_t elf_class = get_elf_class(ehdr, sizeof(ehdr));
219 int32_t elf_type = get_elf_type(ehdr, sizeof(ehdr), elf_class);
220 if (elf_type != ET_DYN) {
221 tloge("not support elf_type:0x%x\n", elf_type);
222 goto close_fd;
223 }
224
225 if (elf_class == ELFCLASS64)
226 loader = ELF_TARUNNER;
227 else
228 loader = ELF_TARUNNER_A32;
229
230 close_fd:
231 close(fd);
232
233 return loader;
234 }
235
236 #define MAX_WAIT_RETRY_COUNT 16
wait_drv_spawn_msg(uint32_t taskid)237 static int32_t wait_drv_spawn_msg(uint32_t taskid)
238 {
239 struct src_msginfo info = { 0 }; /* store sender msg info */
240 struct spawn_sync_msg msg = { 0 };
241
242 int32_t ret;
243 uint32_t retry_count = 0;
244
245 wait_retry:
246 /*
247 * To prevent other process send invalid msg, drvmgr should retry when the sender pid
248 * not match the spawn process pid.
249 * And in order to prevent other process send msg all the time, drvmgr will return fail
250 * when retry MAX_WAIT_RETRY_COUNT.
251 */
252 if (retry_count >= MAX_WAIT_RETRY_COUNT) {
253 tloge("wait drv msg retry_count:%u, should retry fail\n", retry_count);
254 return -1;
255 }
256
257 retry_count++;
258 ret = ipc_msg_receive(g_drv_spawn_sync_channel, &msg, sizeof(msg), g_drv_spawn_sync_msghdl,
259 &info, WAIT_DRV_MSG_MAX_TIME);
260 if (ret == E_EX_TIMER_TIMEOUT) {
261 tloge("wait drv:0x%x spawn msg timeout:%u\n", taskid, WAIT_DRV_MSG_MAX_TIME);
262 return -1;
263 }
264
265 if (ret != 0) {
266 tloge("get drv:0x%x msg receiver fail ret:0x%x\n", taskid, ret);
267 return -1;
268 }
269
270 if (info.src_pid != taskid_to_pid(taskid)) {
271 tloge("sender:0x%x is not spawn process:0x%x, just wait again\n",
272 info.src_pid, taskid);
273 goto wait_retry;
274 }
275
276 if (msg.msg_id != PROCESS_INIT_SUCC) {
277 tloge("spawn task:0x%x init fail\n", taskid);
278 return -1;
279 }
280
281 tlogd("get drv:0x%x spawn msg succ\n", taskid);
282
283 return 0;
284 }
285
286 #define DRV_KILL_WAIT_MAX_COUNT 5
drv_kill_task(uint32_t taskid)287 void drv_kill_task(uint32_t taskid)
288 {
289 if (kill((pid_t)taskid_to_pid(taskid), 0) == 0) {
290 int32_t i;
291 int32_t status;
292 for (i = 0; i < DRV_KILL_WAIT_MAX_COUNT; i++) {
293 if (wait(&status) == (pid_t)taskid_to_pid(taskid)) {
294 tloge("wait drv:0x%x exit succ\n", taskid);
295 break;
296 }
297 }
298
299 if (i == DRV_KILL_WAIT_MAX_COUNT)
300 tloge("wait drv:0x%x exit failed\n", taskid);
301 } else {
302 tloge("kill drv:0x%x failed\n", taskid);
303 }
304 }
305
prepare_spawn_params(const struct drv_spawn_param * drv_param,uint32_t * taskid)306 static int32_t prepare_spawn_params(const struct drv_spawn_param *drv_param, uint32_t *taskid)
307 {
308 int32_t func_ret = -1;
309 int32_t ret = drv_mutex_lock(&g_drv_spawn_mtx);
310 if (ret != 0) {
311 tloge("get drv spawn mtx failed\n");
312 return -1;
313 }
314
315 /* make spawn_buffer in one page */
316 struct spawn_drv_buffer *spawn_buffer = (struct spawn_drv_buffer *)g_spawn_buffer;
317 (void)memset_s(g_spawn_buffer, sizeof(g_spawn_buffer), 0, sizeof(g_spawn_buffer));
318 if (((uintptr_t)g_spawn_buffer & (PAGE_SIZE - 1)) + sizeof(struct spawn_drv_buffer) > PAGE_SIZE)
319 spawn_buffer = (struct spawn_drv_buffer *)(g_spawn_buffer + sizeof(struct spawn_drv_buffer));
320
321 init_spawn_buffer(spawn_buffer);
322
323 if (init_spawn_env(drv_param, spawn_buffer) != 0)
324 goto unlock_spawn_mtx;
325
326 char drv_path[ARGV_SIZE] = { 0 };
327 ret = drv_name_to_path(drv_param, drv_path, sizeof(drv_path));
328 if (ret != 0)
329 goto unlock_spawn_mtx;
330
331 ret = init_spawn_argv(drv_param->drv_name, (strlen(drv_param->drv_name) + 1),
332 drv_path, (strlen(drv_path) + 1), &(spawn_buffer->argv));
333 if (ret != 0)
334 goto unlock_spawn_mtx;
335
336 int32_t loader_type = get_drv_loader(drv_path);
337 if (loader_type == ELF_NOT_SUPPORT) {
338 tloge("get drv loader ret:0x%x fail\n", loader_type);
339 goto unlock_spawn_mtx;
340 }
341
342 ret = spawn_driver(drv_param, loader_type, g_argv, g_env, taskid);
343 if (ret != 0)
344 goto unlock_spawn_mtx;
345
346 ret = wait_drv_spawn_msg(*taskid);
347 if (ret != 0) {
348 tloge("wait drv spawn msg fail\n");
349 drv_kill_task(*taskid);
350 goto unlock_spawn_mtx;
351 }
352
353 func_ret = 0;
354
355 unlock_spawn_mtx:
356 ret = pthread_mutex_unlock(&g_drv_spawn_mtx);
357 if (ret != 0)
358 tloge("something wrong, unlock mtx in drv spawn fail:0x%x\n", ret);
359
360 return func_ret;
361 }
362
set_drv_name_and_uuid(const struct task_node * node,struct drv_spawn_param * param)363 static int32_t set_drv_name_and_uuid(const struct task_node *node, struct drv_spawn_param *param)
364 {
365 if (memcpy_s(¶m->drv_name, (sizeof(param->drv_name) - 1),
366 node->tlv.drv_conf->mani.service_name, node->tlv.drv_conf->mani.service_name_size) != 0) {
367 tloge("copy name:%s fail\n", node->tlv.drv_conf->mani.service_name);
368 return -1;
369 }
370
371 param->drv_name[node->tlv.drv_conf->mani.service_name_size] = '\0';
372
373 if (memcpy_s(¶m->uuid, sizeof(param->uuid),
374 &node->tlv.uuid, sizeof(node->tlv.uuid)) != 0) {
375 tloge("copy uuid:0x%x fail\n", node->tlv.uuid.timeLow);
376 return -1;
377 }
378
379 return 0;
380 }
381
set_drv_thread_limit(const struct task_node * node,struct drv_spawn_param * param)382 static void set_drv_thread_limit(const struct task_node *node, struct drv_spawn_param *param)
383 {
384 param->thread_limit = node->tlv.drv_conf->drv_basic_info.thread_limit;
385 tlogd("thread limit:%u\n", param->thread_limit);
386 }
387
set_drv_stack_size(const struct task_node * node,struct drv_spawn_param * param)388 static int32_t set_drv_stack_size(const struct task_node *node, struct drv_spawn_param *param)
389 {
390 uint32_t stack_size = node->tlv.drv_conf->mani.stack_size;
391 uint32_t stack_size_align = PAGE_ALIGN_UP(stack_size);
392 if (stack_size_align < stack_size) {
393 tloge("invalid stack_size:0x%x\n", stack_size);
394 return -1;
395 }
396
397 if (stack_size_align < DRV_DEFAULT_STACK_SIZE) {
398 tloge("stack_size:0x%x use default:0x%x", stack_size_align, DRV_DEFAULT_STACK_SIZE);
399 stack_size_align = DRV_DEFAULT_STACK_SIZE;
400 }
401
402 param->stack_size = stack_size_align;
403
404 return 0;
405 }
406
set_drv_heap_size(const struct task_node * node,struct drv_spawn_param * param)407 static int32_t set_drv_heap_size(const struct task_node *node, struct drv_spawn_param *param)
408 {
409 uint32_t heap_size = node->tlv.drv_conf->mani.data_size;
410 uint32_t heap_size_align = PAGE_ALIGN_UP(heap_size);
411 if (heap_size_align < heap_size) {
412 tloge("invalid heap_size:0x%x\n", heap_size);
413 return -1;
414 }
415
416 uint32_t stack_size = param->stack_size;
417 uint32_t thread_limit = param->thread_limit;
418 uint32_t extra_stack_size = stack_size * thread_limit;
419 if (extra_stack_size < stack_size) {
420 tloge("stack_size:0x%x and thread_limit:%u is overflow\n", stack_size, thread_limit);
421 return -1;
422 }
423
424 if (heap_size_align + extra_stack_size < heap_size_align) {
425 tloge("heap_size:0x%x and extra_stack_size:0x%x is overflow\n", heap_size_align, extra_stack_size);
426 return -1;
427 }
428
429 heap_size_align += extra_stack_size;
430 param->heap_size = heap_size_align;
431
432 return 0;
433 }
434
get_drv_channel(const char * drv_name,cref_t * ch)435 static int32_t get_drv_channel(const char *drv_name, cref_t *ch)
436 {
437 cref_t channel;
438 int32_t ret = ipc_get_ch_from_path(drv_name, &channel);
439 if (ret != 0) {
440 tloge("get drv:%s channel fail:0x%x\n", drv_name, ret);
441 return -1;
442 }
443
444 tlogd("get drv:%s channel:0x%llx succ\n", drv_name, channel);
445
446 *ch = channel;
447
448 return 0;
449 }
450
send_cmd_perm_msg(uint64_t drv_vaddr,uint32_t drv_size,cref_t channel)451 static int32_t send_cmd_perm_msg(uint64_t drv_vaddr, uint32_t drv_size, cref_t channel)
452 {
453 char buf[SYSCAL_MSG_BUFFER_SIZE] = { 0 };
454 struct drv_req_msg_t *msg = (struct drv_req_msg_t *)buf;
455 struct drv_reply_msg_t *rmsg = (struct drv_reply_msg_t *)buf;
456
457 msg->args[DRV_REGISTER_CMD_ADDR_INDEX] = drv_vaddr;
458 msg->args[DRV_REGISTER_CMD_SIZE_INDEX] = drv_size;
459
460 msg->header.send.msg_id = REGISTER_DRV_CMD_PERM;
461 msg->header.send.msg_size = sizeof(struct drv_req_msg_t);
462
463 int32_t ret = ipc_msg_call(channel, msg, msg->header.send.msg_size, rmsg, SYSCAL_MSG_BUFFER_SIZE, -1);
464 if (ret != 0) {
465 tloge("msg call:0x%x fail ret:0x%x\n", REGISTER_DRV_CMD_PERM, ret);
466 return -1;
467 }
468
469 return rmsg->header.reply.ret_val;
470 }
471
send_cmd_perm_to_drv(const struct task_node * node)472 static int32_t send_cmd_perm_to_drv(const struct task_node *node)
473 {
474 int32_t ret = -1;
475 uint32_t self_pid = get_self_taskid();
476 if (self_pid < 0) {
477 tloge("get self pid fail\n");
478 return ret;
479 }
480
481 if (node->tlv.drv_conf->cmd_perm_list_size == 0) {
482 tlogd("no cmd perm, just return\n");
483 return 0;
484 }
485
486 uint32_t tmp_size = node->tlv.drv_conf->cmd_perm_list_size * sizeof(struct drv_cmd_perm_info_t);
487 void *tmp_addr = alloc_sharemem_aux(&node->tlv.uuid, tmp_size);
488 if (tmp_addr == NULL) {
489 tloge("alloc share mem:0x%x fail\n", tmp_size);
490 return ret;
491 }
492
493 if (memcpy_s(tmp_addr, tmp_size, node->tlv.drv_conf->cmd_perm_list, tmp_size) != 0) {
494 tloge("copy cmd perm to share mem fail\n");
495 goto free_addr;
496 }
497
498 ret = send_cmd_perm_msg((uint64_t)(uintptr_t)tmp_addr, tmp_size, node->drv_task.channel);
499
500 free_addr:
501 if (free_sharemem(tmp_addr, tmp_size) != 0)
502 tloge("free share mem fail\n");
503
504 return ret;
505 }
506
spawn_driver_handle(struct task_node * node)507 int32_t spawn_driver_handle(struct task_node *node)
508 {
509 if (node == NULL || node->tlv.drv_conf == NULL || node->drv_task.drv_index < 0) {
510 tloge("spawn invalid node\n");
511 return -1;
512 }
513
514 struct drv_spawn_param param;
515 (void)memset_s(¶m, sizeof(param), 0, sizeof(param));
516 param.drv_index = (uint32_t)node->drv_task.drv_index;
517
518 if (set_drv_name_and_uuid(node, ¶m) != 0)
519 return -1;
520
521 set_drv_thread_limit(node, ¶m);
522
523 if (set_drv_stack_size(node, ¶m) != 0)
524 return -1;
525
526 /* set heap size must call after set_drv_stack_size and set thread_limit */
527 if (set_drv_heap_size(node, ¶m) != 0)
528 return -1;
529
530 uint32_t taskid;
531 int32_t ret = prepare_spawn_params(¶m, &taskid);
532 if (ret != 0) {
533 tloge("spawn drv:%s fail\n", param.drv_name);
534 return -1;
535 }
536
537 ret = get_drv_channel(param.drv_name, &node->drv_task.channel);
538 if (ret != 0)
539 goto kill_task;
540
541 ret = send_cmd_perm_to_drv(node);
542 if (ret != 0)
543 goto release_channel;
544
545 node->pid = taskid_to_pid(taskid);
546
547 return 0;
548
549 release_channel:
550 if (ipc_release_from_path(node->tlv.drv_conf->mani.service_name, node->drv_task.channel) != 0)
551 tloge("release drv:%s channel:0x%llx failed\n", node->tlv.drv_conf->mani.service_name, node->drv_task.channel);
552 node->drv_task.channel = -1;
553
554 kill_task:
555 drv_kill_task(taskid);
556
557 return -1;
558 }
559
release_driver(struct task_node * node)560 void release_driver(struct task_node *node)
561 {
562 if (node == NULL || node->target_type != DRV_TARGET_TYPE || node->tlv.drv_conf == NULL) {
563 tloge("invalid node\n");
564 return;
565 }
566
567 if (node->drv_task.register_policy) {
568 del_dynamic_policy_to_drv(&node->tlv.uuid);
569 node->drv_task.register_policy = false;
570 }
571
572 if (check_ref_valid(node->drv_task.channel)) {
573 if (ipc_release_from_path(node->tlv.drv_conf->mani.service_name, node->drv_task.channel) != 0)
574 tloge("release drv:%s channel:0x%llx failed\n",
575 node->tlv.drv_conf->mani.service_name, node->drv_task.channel);
576 node->drv_task.channel = -1;
577 }
578
579 if (node->pid != (uint32_t)INVALID_CALLER_PID) {
580 drv_kill_task(node->pid);
581 node->pid = (uint32_t)INVALID_CALLER_PID;
582 }
583 }
584