1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 #include "api.h"
3 #include "dsp/base.h"
4
5 #include <assert.h>
6 #include <libpldm/base.h>
7 #include <libpldm/pldm_types.h>
8
9 #include <endian.h>
10 #include <errno.h>
11 #include <stdint.h>
12 #include <string.h>
13
pack_pldm_header_errno(const struct pldm_header_info * hdr,struct pldm_msg_hdr * msg)14 int pack_pldm_header_errno(const struct pldm_header_info *hdr,
15 struct pldm_msg_hdr *msg)
16 {
17 if (msg == NULL || hdr == NULL) {
18 return -EINVAL;
19 }
20
21 if (hdr->msg_type != PLDM_RESPONSE && hdr->msg_type != PLDM_REQUEST &&
22 hdr->msg_type != PLDM_ASYNC_REQUEST_NOTIFY) {
23 return -EINVAL;
24 }
25
26 if (hdr->instance > PLDM_INSTANCE_MAX) {
27 return -EINVAL;
28 }
29
30 if (hdr->pldm_type > (PLDM_MAX_TYPES - 1)) {
31 return -ENOMSG;
32 }
33
34 uint8_t datagram = (hdr->msg_type == PLDM_ASYNC_REQUEST_NOTIFY) ? 1 : 0;
35
36 if (hdr->msg_type == PLDM_RESPONSE) {
37 msg->request = PLDM_RESPONSE;
38 } else if (hdr->msg_type == PLDM_REQUEST ||
39 hdr->msg_type == PLDM_ASYNC_REQUEST_NOTIFY) {
40 msg->request = PLDM_REQUEST;
41 }
42 msg->datagram = datagram;
43 msg->reserved = 0;
44 msg->instance_id = hdr->instance;
45 msg->header_ver = PLDM_CURRENT_VERSION;
46 msg->type = hdr->pldm_type;
47 msg->command = hdr->command;
48
49 return 0;
50 }
51
unpack_pldm_header_errno(const struct pldm_msg_hdr * msg,struct pldm_header_info * hdr)52 int unpack_pldm_header_errno(const struct pldm_msg_hdr *msg,
53 struct pldm_header_info *hdr)
54 {
55 if (msg == NULL) {
56 return -EINVAL;
57 }
58
59 if (msg->request == PLDM_RESPONSE) {
60 hdr->msg_type = PLDM_RESPONSE;
61 } else {
62 hdr->msg_type = msg->datagram ? PLDM_ASYNC_REQUEST_NOTIFY :
63 PLDM_REQUEST;
64 }
65
66 hdr->instance = msg->instance_id;
67 hdr->pldm_type = msg->type;
68 hdr->command = msg->command;
69
70 return 0;
71 }
72
73 LIBPLDM_ABI_STABLE
pack_pldm_header(const struct pldm_header_info * hdr,struct pldm_msg_hdr * msg)74 uint8_t pack_pldm_header(const struct pldm_header_info *hdr,
75 struct pldm_msg_hdr *msg)
76 {
77 enum pldm_completion_codes cc;
78 int rc;
79
80 rc = pack_pldm_header_errno(hdr, msg);
81 if (!rc) {
82 return PLDM_SUCCESS;
83 }
84
85 cc = pldm_xlate_errno(rc);
86 assert(cc < UINT8_MAX);
87 if (cc > UINT8_MAX) {
88 static_assert(PLDM_ERROR < UINT8_MAX, "Unable to report error");
89 return PLDM_ERROR;
90 }
91
92 return cc;
93 }
94
95 LIBPLDM_ABI_STABLE
unpack_pldm_header(const struct pldm_msg_hdr * msg,struct pldm_header_info * hdr)96 uint8_t unpack_pldm_header(const struct pldm_msg_hdr *msg,
97 struct pldm_header_info *hdr)
98 {
99 enum pldm_completion_codes cc;
100 int rc;
101
102 rc = unpack_pldm_header_errno(msg, hdr);
103 if (!rc) {
104 return PLDM_SUCCESS;
105 }
106
107 cc = pldm_xlate_errno(rc);
108 assert(cc < UINT8_MAX);
109 if (cc > UINT8_MAX) {
110 static_assert(PLDM_ERROR < UINT8_MAX, "Unable to report error");
111 return PLDM_ERROR;
112 }
113
114 return cc;
115 }
116
117 LIBPLDM_ABI_STABLE
pldm_msg_hdr_correlate_response(const struct pldm_msg_hdr * req,const struct pldm_msg_hdr * resp)118 bool pldm_msg_hdr_correlate_response(const struct pldm_msg_hdr *req,
119 const struct pldm_msg_hdr *resp)
120 {
121 return req->instance_id == resp->instance_id && req->request &&
122 !resp->request && req->type == resp->type &&
123 req->command == resp->command;
124 }
125
126 LIBPLDM_ABI_STABLE
encode_get_types_req(uint8_t instance_id,struct pldm_msg * msg)127 int encode_get_types_req(uint8_t instance_id, struct pldm_msg *msg)
128 {
129 if (msg == NULL) {
130 return PLDM_ERROR_INVALID_DATA;
131 }
132
133 struct pldm_header_info header = { 0 };
134 header.instance = instance_id;
135 header.msg_type = PLDM_REQUEST;
136 header.command = PLDM_GET_PLDM_TYPES;
137
138 return pack_pldm_header(&header, &(msg->hdr));
139 }
140
141 LIBPLDM_ABI_STABLE
encode_get_commands_req(uint8_t instance_id,uint8_t type,ver32_t version,struct pldm_msg * msg)142 int encode_get_commands_req(uint8_t instance_id, uint8_t type, ver32_t version,
143 struct pldm_msg *msg)
144 {
145 if (msg == NULL) {
146 return PLDM_ERROR_INVALID_DATA;
147 }
148
149 struct pldm_header_info header = { 0 };
150 header.instance = instance_id;
151 header.msg_type = PLDM_REQUEST;
152 header.command = PLDM_GET_PLDM_COMMANDS;
153
154 uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
155 if (rc != PLDM_SUCCESS) {
156 return rc;
157 }
158
159 struct pldm_get_commands_req *request =
160 (struct pldm_get_commands_req *)msg->payload;
161
162 request->type = type;
163 request->version = version;
164
165 return PLDM_SUCCESS;
166 }
167
168 LIBPLDM_ABI_STABLE
encode_get_types_resp(uint8_t instance_id,uint8_t completion_code,const bitfield8_t * types,struct pldm_msg * msg)169 int encode_get_types_resp(uint8_t instance_id, uint8_t completion_code,
170 const bitfield8_t *types, struct pldm_msg *msg)
171 {
172 if (msg == NULL) {
173 return PLDM_ERROR_INVALID_DATA;
174 }
175
176 struct pldm_header_info header = { 0 };
177 header.instance = instance_id;
178 header.msg_type = PLDM_RESPONSE;
179 header.command = PLDM_GET_PLDM_TYPES;
180
181 uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
182 if (rc != PLDM_SUCCESS) {
183 return rc;
184 }
185
186 struct pldm_get_types_resp *response =
187 (struct pldm_get_types_resp *)msg->payload;
188 response->completion_code = completion_code;
189 if (response->completion_code == PLDM_SUCCESS) {
190 if (types == NULL) {
191 return PLDM_ERROR_INVALID_DATA;
192 }
193 memcpy(response->types, &(types->byte), PLDM_MAX_TYPES / 8);
194 }
195
196 return PLDM_SUCCESS;
197 }
198
199 LIBPLDM_ABI_STABLE
decode_get_commands_req(const struct pldm_msg * msg,size_t payload_length,uint8_t * type,ver32_t * version)200 int decode_get_commands_req(const struct pldm_msg *msg, size_t payload_length,
201 uint8_t *type, ver32_t *version)
202 {
203 if (msg == NULL || type == NULL || version == NULL) {
204 return PLDM_ERROR_INVALID_DATA;
205 }
206
207 if (payload_length != PLDM_GET_COMMANDS_REQ_BYTES) {
208 return PLDM_ERROR_INVALID_LENGTH;
209 }
210
211 struct pldm_get_commands_req *request =
212 (struct pldm_get_commands_req *)msg->payload;
213 *type = request->type;
214 *version = request->version;
215 return PLDM_SUCCESS;
216 }
217
218 LIBPLDM_ABI_STABLE
encode_get_commands_resp(uint8_t instance_id,uint8_t completion_code,const bitfield8_t * commands,struct pldm_msg * msg)219 int encode_get_commands_resp(uint8_t instance_id, uint8_t completion_code,
220 const bitfield8_t *commands, struct pldm_msg *msg)
221 {
222 if (msg == NULL) {
223 return PLDM_ERROR_INVALID_DATA;
224 }
225
226 struct pldm_header_info header = { 0 };
227 header.instance = instance_id;
228 header.msg_type = PLDM_RESPONSE;
229 header.command = PLDM_GET_PLDM_COMMANDS;
230 uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
231 if (rc != PLDM_SUCCESS) {
232 return rc;
233 }
234
235 struct pldm_get_commands_resp *response =
236 (struct pldm_get_commands_resp *)msg->payload;
237 response->completion_code = completion_code;
238 if (response->completion_code == PLDM_SUCCESS) {
239 if (commands == NULL) {
240 return PLDM_ERROR_INVALID_DATA;
241 }
242 memcpy(response->commands, &(commands->byte),
243 PLDM_MAX_CMDS_PER_TYPE / 8);
244 }
245
246 return PLDM_SUCCESS;
247 }
248
249 LIBPLDM_ABI_STABLE
decode_get_types_resp(const struct pldm_msg * msg,size_t payload_length,uint8_t * completion_code,bitfield8_t * types)250 int decode_get_types_resp(const struct pldm_msg *msg, size_t payload_length,
251 uint8_t *completion_code, bitfield8_t *types)
252 {
253 if (msg == NULL || types == NULL || completion_code == NULL) {
254 return PLDM_ERROR_INVALID_DATA;
255 }
256
257 *completion_code = msg->payload[0];
258 if (PLDM_SUCCESS != *completion_code) {
259 return PLDM_SUCCESS;
260 }
261
262 if (payload_length != PLDM_GET_TYPES_RESP_BYTES) {
263 return PLDM_ERROR_INVALID_LENGTH;
264 }
265
266 struct pldm_get_types_resp *response =
267 (struct pldm_get_types_resp *)msg->payload;
268
269 memcpy(&(types->byte), response->types, PLDM_MAX_TYPES / 8);
270
271 return PLDM_SUCCESS;
272 }
273
274 LIBPLDM_ABI_STABLE
decode_get_commands_resp(const struct pldm_msg * msg,size_t payload_length,uint8_t * completion_code,bitfield8_t * commands)275 int decode_get_commands_resp(const struct pldm_msg *msg, size_t payload_length,
276 uint8_t *completion_code, bitfield8_t *commands)
277 {
278 if (msg == NULL || commands == NULL || completion_code == NULL) {
279 return PLDM_ERROR_INVALID_DATA;
280 }
281
282 *completion_code = msg->payload[0];
283 if (PLDM_SUCCESS != *completion_code) {
284 return PLDM_SUCCESS;
285 }
286
287 if (payload_length != PLDM_GET_COMMANDS_RESP_BYTES) {
288 return PLDM_ERROR_INVALID_LENGTH;
289 }
290
291 struct pldm_get_commands_resp *response =
292 (struct pldm_get_commands_resp *)msg->payload;
293
294 memcpy(&(commands->byte), response->commands,
295 PLDM_MAX_CMDS_PER_TYPE / 8);
296
297 return PLDM_SUCCESS;
298 }
299
300 LIBPLDM_ABI_STABLE
encode_get_version_req(uint8_t instance_id,uint32_t transfer_handle,uint8_t transfer_opflag,uint8_t type,struct pldm_msg * msg)301 int encode_get_version_req(uint8_t instance_id, uint32_t transfer_handle,
302 uint8_t transfer_opflag, uint8_t type,
303 struct pldm_msg *msg)
304 {
305 if (NULL == msg) {
306 return PLDM_ERROR_INVALID_DATA;
307 }
308
309 struct pldm_header_info header = { 0 };
310 header.msg_type = PLDM_REQUEST;
311 header.instance = instance_id;
312 header.pldm_type = PLDM_BASE;
313 header.command = PLDM_GET_PLDM_VERSION;
314
315 uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
316 if (rc != PLDM_SUCCESS) {
317 return rc;
318 }
319
320 struct pldm_get_version_req *request =
321 (struct pldm_get_version_req *)msg->payload;
322 transfer_handle = htole32(transfer_handle);
323 request->transfer_handle = transfer_handle;
324 request->transfer_opflag = transfer_opflag;
325 request->type = type;
326
327 return PLDM_SUCCESS;
328 }
329
330 LIBPLDM_ABI_DEPRECATED_UNSAFE
encode_get_version_resp(uint8_t instance_id,uint8_t completion_code,uint32_t next_transfer_handle,uint8_t transfer_flag,const ver32_t * version_data,size_t version_size,struct pldm_msg * msg)331 int encode_get_version_resp(uint8_t instance_id, uint8_t completion_code,
332 uint32_t next_transfer_handle,
333 uint8_t transfer_flag, const ver32_t *version_data,
334 size_t version_size, struct pldm_msg *msg)
335 {
336 if (NULL == msg || NULL == version_data) {
337 return PLDM_ERROR_INVALID_DATA;
338 }
339
340 struct pldm_header_info header = { 0 };
341 header.msg_type = PLDM_RESPONSE;
342 header.instance = instance_id;
343 header.pldm_type = PLDM_BASE;
344 header.command = PLDM_GET_PLDM_VERSION;
345
346 uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
347 if (rc != PLDM_SUCCESS) {
348 return rc;
349 }
350
351 struct pldm_get_version_resp *response =
352 (struct pldm_get_version_resp *)msg->payload;
353 response->completion_code = completion_code;
354 if (response->completion_code == PLDM_SUCCESS) {
355 response->next_transfer_handle = htole32(next_transfer_handle);
356 response->transfer_flag = transfer_flag;
357 memcpy(response->version_data, (uint8_t *)version_data,
358 version_size);
359 }
360 return PLDM_SUCCESS;
361 }
362
363 LIBPLDM_ABI_STABLE
decode_get_version_req(const struct pldm_msg * msg,size_t payload_length,uint32_t * transfer_handle,uint8_t * transfer_opflag,uint8_t * type)364 int decode_get_version_req(const struct pldm_msg *msg, size_t payload_length,
365 uint32_t *transfer_handle, uint8_t *transfer_opflag,
366 uint8_t *type)
367 {
368 if (payload_length != PLDM_GET_VERSION_REQ_BYTES) {
369 return PLDM_ERROR_INVALID_LENGTH;
370 }
371
372 struct pldm_get_version_req *request =
373 (struct pldm_get_version_req *)msg->payload;
374 *transfer_handle = le32toh(request->transfer_handle);
375 *transfer_opflag = request->transfer_opflag;
376 *type = request->type;
377 return PLDM_SUCCESS;
378 }
379
380 LIBPLDM_ABI_STABLE
decode_get_version_resp(const struct pldm_msg * msg,size_t payload_length,uint8_t * completion_code,uint32_t * next_transfer_handle,uint8_t * transfer_flag,ver32_t * version)381 int decode_get_version_resp(const struct pldm_msg *msg, size_t payload_length,
382 uint8_t *completion_code,
383 uint32_t *next_transfer_handle,
384 uint8_t *transfer_flag, ver32_t *version)
385 {
386 if (msg == NULL || next_transfer_handle == NULL ||
387 transfer_flag == NULL || completion_code == NULL) {
388 return PLDM_ERROR_INVALID_DATA;
389 }
390
391 *completion_code = msg->payload[0];
392 if (PLDM_SUCCESS != *completion_code) {
393 return PLDM_SUCCESS;
394 }
395
396 if (payload_length < PLDM_GET_VERSION_RESP_BYTES) {
397 return PLDM_ERROR_INVALID_LENGTH;
398 }
399
400 struct pldm_get_version_resp *response =
401 (struct pldm_get_version_resp *)msg->payload;
402
403 *next_transfer_handle = le32toh(response->next_transfer_handle);
404 *transfer_flag = response->transfer_flag;
405 memcpy(version, (uint8_t *)response->version_data, sizeof(ver32_t));
406
407 return PLDM_SUCCESS;
408 }
409
410 LIBPLDM_ABI_STABLE
encode_get_tid_req(uint8_t instance_id,struct pldm_msg * msg)411 int encode_get_tid_req(uint8_t instance_id, struct pldm_msg *msg)
412 {
413 if (msg == NULL) {
414 return PLDM_ERROR_INVALID_DATA;
415 }
416
417 struct pldm_header_info header = { 0 };
418 header.instance = instance_id;
419 header.msg_type = PLDM_REQUEST;
420 header.command = PLDM_GET_TID;
421
422 return pack_pldm_header(&header, &(msg->hdr));
423 }
424
425 LIBPLDM_ABI_STABLE
encode_get_tid_resp(uint8_t instance_id,uint8_t completion_code,uint8_t tid,struct pldm_msg * msg)426 int encode_get_tid_resp(uint8_t instance_id, uint8_t completion_code,
427 uint8_t tid, struct pldm_msg *msg)
428 {
429 if (msg == NULL) {
430 return PLDM_ERROR_INVALID_DATA;
431 }
432
433 struct pldm_header_info header = { 0 };
434 header.instance = instance_id;
435 header.msg_type = PLDM_RESPONSE;
436 header.command = PLDM_GET_TID;
437
438 uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
439 if (rc != PLDM_SUCCESS) {
440 return rc;
441 }
442
443 struct pldm_get_tid_resp *response =
444 (struct pldm_get_tid_resp *)msg->payload;
445 response->completion_code = completion_code;
446 response->tid = tid;
447
448 return PLDM_SUCCESS;
449 }
450
451 LIBPLDM_ABI_STABLE
decode_get_tid_resp(const struct pldm_msg * msg,size_t payload_length,uint8_t * completion_code,uint8_t * tid)452 int decode_get_tid_resp(const struct pldm_msg *msg, size_t payload_length,
453 uint8_t *completion_code, uint8_t *tid)
454 {
455 if (msg == NULL || tid == NULL || completion_code == NULL) {
456 return PLDM_ERROR_INVALID_DATA;
457 }
458
459 *completion_code = msg->payload[0];
460 if (PLDM_SUCCESS != *completion_code) {
461 return PLDM_SUCCESS;
462 }
463
464 if (payload_length != PLDM_GET_TID_RESP_BYTES) {
465 return PLDM_ERROR_INVALID_LENGTH;
466 }
467
468 struct pldm_get_tid_resp *response =
469 (struct pldm_get_tid_resp *)msg->payload;
470
471 *tid = response->tid;
472
473 return PLDM_SUCCESS;
474 }
475
476 LIBPLDM_ABI_STABLE
encode_set_tid_req(uint8_t instance_id,uint8_t tid,struct pldm_msg * msg)477 int encode_set_tid_req(uint8_t instance_id, uint8_t tid, struct pldm_msg *msg)
478 {
479 if (msg == NULL) {
480 return PLDM_ERROR_INVALID_DATA;
481 }
482
483 if (tid == 0x0 || tid == 0xff) {
484 return PLDM_ERROR_INVALID_DATA;
485 }
486
487 struct pldm_header_info header = { 0 };
488 header.instance = instance_id;
489 header.msg_type = PLDM_REQUEST;
490 header.command = PLDM_SET_TID;
491
492 uint8_t rc = pack_pldm_header(&header, &(msg->hdr));
493 if (rc != PLDM_SUCCESS) {
494 return rc;
495 }
496
497 struct pldm_set_tid_req *request =
498 (struct pldm_set_tid_req *)msg->payload;
499 request->tid = tid;
500
501 return PLDM_SUCCESS;
502 }
503
504 LIBPLDM_ABI_STABLE
decode_multipart_receive_req(const struct pldm_msg * msg,size_t payload_length,uint8_t * pldm_type,uint8_t * transfer_opflag,uint32_t * transfer_ctx,uint32_t * transfer_handle,uint32_t * section_offset,uint32_t * section_length)505 int decode_multipart_receive_req(const struct pldm_msg *msg,
506 size_t payload_length, uint8_t *pldm_type,
507 uint8_t *transfer_opflag,
508 uint32_t *transfer_ctx,
509 uint32_t *transfer_handle,
510 uint32_t *section_offset,
511 uint32_t *section_length)
512 {
513 if (msg == NULL || pldm_type == NULL || transfer_opflag == NULL ||
514 transfer_ctx == NULL || transfer_handle == NULL ||
515 section_offset == NULL || section_length == NULL) {
516 return PLDM_ERROR_INVALID_DATA;
517 }
518
519 if (payload_length != PLDM_MULTIPART_RECEIVE_REQ_BYTES) {
520 return PLDM_ERROR_INVALID_LENGTH;
521 }
522
523 struct pldm_multipart_receive_req *request =
524 (struct pldm_multipart_receive_req *)msg->payload;
525
526 if (request->pldm_type != PLDM_BASE) {
527 return PLDM_ERROR_INVALID_PLDM_TYPE;
528 }
529
530 // Any enum value above PLDM_XFER_CURRENT_PART is invalid.
531 if (request->transfer_opflag > PLDM_XFER_CURRENT_PART) {
532 return PLDM_INVALID_TRANSFER_OPERATION_FLAG;
533 }
534
535 // A section offset of 0 is only valid on FIRST_PART or COMPLETE Xfers.
536 uint32_t sec_offset = le32toh(request->section_offset);
537 if (sec_offset == 0 &&
538 (request->transfer_opflag != PLDM_XFER_FIRST_PART &&
539 request->transfer_opflag != PLDM_XFER_COMPLETE)) {
540 return PLDM_ERROR_INVALID_DATA;
541 }
542
543 uint32_t handle = le32toh(request->transfer_handle);
544 if (handle == 0 && request->transfer_opflag != PLDM_XFER_COMPLETE) {
545 return PLDM_ERROR_INVALID_DATA;
546 }
547
548 *pldm_type = request->pldm_type;
549 *transfer_opflag = request->transfer_opflag;
550 *transfer_ctx = request->transfer_ctx;
551 *transfer_handle = handle;
552 *section_offset = sec_offset;
553 *section_length = le32toh(request->section_length);
554
555 return PLDM_SUCCESS;
556 }
557
558 LIBPLDM_ABI_STABLE
encode_cc_only_resp(uint8_t instance_id,uint8_t type,uint8_t command,uint8_t cc,struct pldm_msg * msg)559 int encode_cc_only_resp(uint8_t instance_id, uint8_t type, uint8_t command,
560 uint8_t cc, struct pldm_msg *msg)
561 {
562 if (msg == NULL) {
563 return PLDM_ERROR_INVALID_DATA;
564 }
565
566 struct pldm_header_info header = { 0 };
567 header.instance = instance_id;
568 header.msg_type = PLDM_RESPONSE;
569 header.pldm_type = type;
570 header.command = command;
571
572 uint8_t rc = pack_pldm_header(&header, &msg->hdr);
573 if (rc != PLDM_SUCCESS) {
574 return rc;
575 }
576
577 msg->payload[0] = cc;
578
579 return PLDM_SUCCESS;
580 }
581
encode_pldm_header_only_errno(uint8_t msg_type,uint8_t instance_id,uint8_t pldm_type,uint8_t command,struct pldm_msg * msg)582 int encode_pldm_header_only_errno(uint8_t msg_type, uint8_t instance_id,
583 uint8_t pldm_type, uint8_t command,
584 struct pldm_msg *msg)
585 {
586 if (msg == NULL) {
587 return -EINVAL;
588 }
589
590 struct pldm_header_info header = { 0 };
591 header.msg_type = msg_type;
592 header.instance = instance_id;
593 header.pldm_type = pldm_type;
594 header.command = command;
595 return pack_pldm_header_errno(&header, &(msg->hdr));
596 }
597
598 LIBPLDM_ABI_STABLE
encode_pldm_header_only(uint8_t msg_type,uint8_t instance_id,uint8_t pldm_type,uint8_t command,struct pldm_msg * msg)599 int encode_pldm_header_only(uint8_t msg_type, uint8_t instance_id,
600 uint8_t pldm_type, uint8_t command,
601 struct pldm_msg *msg)
602 {
603 int rc = encode_pldm_header_only_errno(msg_type, instance_id, pldm_type,
604 command, msg);
605 if (rc) {
606 return pldm_xlate_errno(rc);
607 }
608 return PLDM_SUCCESS;
609 }
610