xref: /freebsd/sys/dev/e1000/e1000_manage.c (revision 4d846d26)
1 /******************************************************************************
2   SPDX-License-Identifier: BSD-3-Clause
3 
4   Copyright (c) 2001-2020, Intel Corporation
5   All rights reserved.
6 
7   Redistribution and use in source and binary forms, with or without
8   modification, are permitted provided that the following conditions are met:
9 
10    1. Redistributions of source code must retain the above copyright notice,
11       this list of conditions and the following disclaimer.
12 
13    2. Redistributions in binary form must reproduce the above copyright
14       notice, this list of conditions and the following disclaimer in the
15       documentation and/or other materials provided with the distribution.
16 
17    3. Neither the name of the Intel Corporation nor the names of its
18       contributors may be used to endorse or promote products derived from
19       this software without specific prior written permission.
20 
21   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31   POSSIBILITY OF SUCH DAMAGE.
32 
33 ******************************************************************************/
34 /*$FreeBSD$*/
35 
36 #include "e1000_api.h"
37 #include "e1000_manage.h"
38 
39 /**
40  *  e1000_calculate_checksum - Calculate checksum for buffer
41  *  @buffer: pointer to EEPROM
42  *  @length: size of EEPROM to calculate a checksum for
43  *
44  *  Calculates the checksum for some buffer on a specified length.  The
45  *  checksum calculated is returned.
46  **/
47 u8 e1000_calculate_checksum(u8 *buffer, u32 length)
48 {
49 	u32 i;
50 	u8 sum = 0;
51 
52 	DEBUGFUNC("e1000_calculate_checksum");
53 
54 	if (!buffer)
55 		return 0;
56 
57 	for (i = 0; i < length; i++)
58 		sum += buffer[i];
59 
60 	return (u8) (0 - sum);
61 }
62 
63 /**
64  *  e1000_mng_enable_host_if_generic - Checks host interface is enabled
65  *  @hw: pointer to the HW structure
66  *
67  *  Returns E1000_success upon success, else E1000_ERR_HOST_INTERFACE_COMMAND
68  *
69  *  This function checks whether the HOST IF is enabled for command operation
70  *  and also checks whether the previous command is completed.  It busy waits
71  *  in case of previous command is not completed.
72  **/
73 s32 e1000_mng_enable_host_if_generic(struct e1000_hw *hw)
74 {
75 	u32 hicr;
76 	u8 i;
77 
78 	DEBUGFUNC("e1000_mng_enable_host_if_generic");
79 
80 	if (!hw->mac.arc_subsystem_valid) {
81 		DEBUGOUT("ARC subsystem not valid.\n");
82 		return -E1000_ERR_HOST_INTERFACE_COMMAND;
83 	}
84 
85 	/* Check that the host interface is enabled. */
86 	hicr = E1000_READ_REG(hw, E1000_HICR);
87 	if (!(hicr & E1000_HICR_EN)) {
88 		DEBUGOUT("E1000_HOST_EN bit disabled.\n");
89 		return -E1000_ERR_HOST_INTERFACE_COMMAND;
90 	}
91 	/* check the previous command is completed */
92 	for (i = 0; i < E1000_MNG_DHCP_COMMAND_TIMEOUT; i++) {
93 		hicr = E1000_READ_REG(hw, E1000_HICR);
94 		if (!(hicr & E1000_HICR_C))
95 			break;
96 		msec_delay_irq(1);
97 	}
98 
99 	if (i == E1000_MNG_DHCP_COMMAND_TIMEOUT) {
100 		DEBUGOUT("Previous command timeout failed .\n");
101 		return -E1000_ERR_HOST_INTERFACE_COMMAND;
102 	}
103 
104 	return E1000_SUCCESS;
105 }
106 
107 /**
108  *  e1000_check_mng_mode_generic - Generic check management mode
109  *  @hw: pointer to the HW structure
110  *
111  *  Reads the firmware semaphore register and returns true (>0) if
112  *  manageability is enabled, else false (0).
113  **/
114 bool e1000_check_mng_mode_generic(struct e1000_hw *hw)
115 {
116 	u32 fwsm = E1000_READ_REG(hw, E1000_FWSM);
117 
118 	DEBUGFUNC("e1000_check_mng_mode_generic");
119 
120 
121 	return (fwsm & E1000_FWSM_MODE_MASK) ==
122 		(E1000_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT);
123 }
124 
125 /**
126  *  e1000_enable_tx_pkt_filtering_generic - Enable packet filtering on Tx
127  *  @hw: pointer to the HW structure
128  *
129  *  Enables packet filtering on transmit packets if manageability is enabled
130  *  and host interface is enabled.
131  **/
132 bool e1000_enable_tx_pkt_filtering_generic(struct e1000_hw *hw)
133 {
134 	struct e1000_host_mng_dhcp_cookie *hdr = &hw->mng_cookie;
135 	u32 *buffer = (u32 *)&hw->mng_cookie;
136 	u32 offset;
137 	s32 ret_val, hdr_csum, csum;
138 	u8 i, len;
139 
140 	DEBUGFUNC("e1000_enable_tx_pkt_filtering_generic");
141 
142 	hw->mac.tx_pkt_filtering = true;
143 
144 	/* No manageability, no filtering */
145 	if (!hw->mac.ops.check_mng_mode(hw)) {
146 		hw->mac.tx_pkt_filtering = false;
147 		return hw->mac.tx_pkt_filtering;
148 	}
149 
150 	/* If we can't read from the host interface for whatever
151 	 * reason, disable filtering.
152 	 */
153 	ret_val = e1000_mng_enable_host_if_generic(hw);
154 	if (ret_val != E1000_SUCCESS) {
155 		hw->mac.tx_pkt_filtering = false;
156 		return hw->mac.tx_pkt_filtering;
157 	}
158 
159 	/* Read in the header.  Length and offset are in dwords. */
160 	len    = E1000_MNG_DHCP_COOKIE_LENGTH >> 2;
161 	offset = E1000_MNG_DHCP_COOKIE_OFFSET >> 2;
162 	for (i = 0; i < len; i++)
163 		*(buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw, E1000_HOST_IF,
164 							   offset + i);
165 	hdr_csum = hdr->checksum;
166 	hdr->checksum = 0;
167 	csum = e1000_calculate_checksum((u8 *)hdr,
168 					E1000_MNG_DHCP_COOKIE_LENGTH);
169 	/* If either the checksums or signature don't match, then
170 	 * the cookie area isn't considered valid, in which case we
171 	 * take the safe route of assuming Tx filtering is enabled.
172 	 */
173 	if ((hdr_csum != csum) || (hdr->signature != E1000_IAMT_SIGNATURE)) {
174 		hw->mac.tx_pkt_filtering = true;
175 		return hw->mac.tx_pkt_filtering;
176 	}
177 
178 	/* Cookie area is valid, make the final check for filtering. */
179 	if (!(hdr->status & E1000_MNG_DHCP_COOKIE_STATUS_PARSING))
180 		hw->mac.tx_pkt_filtering = false;
181 
182 	return hw->mac.tx_pkt_filtering;
183 }
184 
185 /**
186  *  e1000_mng_write_cmd_header_generic - Writes manageability command header
187  *  @hw: pointer to the HW structure
188  *  @hdr: pointer to the host interface command header
189  *
190  *  Writes the command header after does the checksum calculation.
191  **/
192 s32 e1000_mng_write_cmd_header_generic(struct e1000_hw *hw,
193 				      struct e1000_host_mng_command_header *hdr)
194 {
195 	u16 i, length = sizeof(struct e1000_host_mng_command_header);
196 
197 	DEBUGFUNC("e1000_mng_write_cmd_header_generic");
198 
199 	/* Write the whole command header structure with new checksum. */
200 
201 	hdr->checksum = e1000_calculate_checksum((u8 *)hdr, length);
202 
203 	length >>= 2;
204 	/* Write the relevant command block into the ram area. */
205 	for (i = 0; i < length; i++) {
206 		E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, i,
207 					    *((u32 *) hdr + i));
208 		E1000_WRITE_FLUSH(hw);
209 	}
210 
211 	return E1000_SUCCESS;
212 }
213 
214 /**
215  *  e1000_mng_host_if_write_generic - Write to the manageability host interface
216  *  @hw: pointer to the HW structure
217  *  @buffer: pointer to the host interface buffer
218  *  @length: size of the buffer
219  *  @offset: location in the buffer to write to
220  *  @sum: sum of the data (not checksum)
221  *
222  *  This function writes the buffer content at the offset given on the host if.
223  *  It also does alignment considerations to do the writes in most efficient
224  *  way.  Also fills up the sum of the buffer in *buffer parameter.
225  **/
226 s32 e1000_mng_host_if_write_generic(struct e1000_hw *hw, u8 *buffer,
227 				    u16 length, u16 offset, u8 *sum)
228 {
229 	u8 *tmp;
230 	u8 *bufptr = buffer;
231 	u32 data = 0;
232 	u16 remaining, i, j, prev_bytes;
233 
234 	DEBUGFUNC("e1000_mng_host_if_write_generic");
235 
236 	/* sum = only sum of the data and it is not checksum */
237 
238 	if (length == 0 || offset + length > E1000_HI_MAX_MNG_DATA_LENGTH)
239 		return -E1000_ERR_PARAM;
240 
241 	tmp = (u8 *)&data;
242 	prev_bytes = offset & 0x3;
243 	offset >>= 2;
244 
245 	if (prev_bytes) {
246 		data = E1000_READ_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset);
247 		for (j = prev_bytes; j < sizeof(u32); j++) {
248 			*(tmp + j) = *bufptr++;
249 			*sum += *(tmp + j);
250 		}
251 		E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset, data);
252 		length -= j - prev_bytes;
253 		offset++;
254 	}
255 
256 	remaining = length & 0x3;
257 	length -= remaining;
258 
259 	/* Calculate length in DWORDs */
260 	length >>= 2;
261 
262 	/* The device driver writes the relevant command block into the
263 	 * ram area.
264 	 */
265 	for (i = 0; i < length; i++) {
266 		for (j = 0; j < sizeof(u32); j++) {
267 			*(tmp + j) = *bufptr++;
268 			*sum += *(tmp + j);
269 		}
270 
271 		E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset + i,
272 					    data);
273 	}
274 	if (remaining) {
275 		for (j = 0; j < sizeof(u32); j++) {
276 			if (j < remaining)
277 				*(tmp + j) = *bufptr++;
278 			else
279 				*(tmp + j) = 0;
280 
281 			*sum += *(tmp + j);
282 		}
283 		E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, offset + i,
284 					    data);
285 	}
286 
287 	return E1000_SUCCESS;
288 }
289 
290 /**
291  *  e1000_mng_write_dhcp_info_generic - Writes DHCP info to host interface
292  *  @hw: pointer to the HW structure
293  *  @buffer: pointer to the host interface
294  *  @length: size of the buffer
295  *
296  *  Writes the DHCP information to the host interface.
297  **/
298 s32 e1000_mng_write_dhcp_info_generic(struct e1000_hw *hw, u8 *buffer,
299 				      u16 length)
300 {
301 	struct e1000_host_mng_command_header hdr;
302 	s32 ret_val;
303 	u32 hicr;
304 
305 	DEBUGFUNC("e1000_mng_write_dhcp_info_generic");
306 
307 	hdr.command_id = E1000_MNG_DHCP_TX_PAYLOAD_CMD;
308 	hdr.command_length = length;
309 	hdr.reserved1 = 0;
310 	hdr.reserved2 = 0;
311 	hdr.checksum = 0;
312 
313 	/* Enable the host interface */
314 	ret_val = e1000_mng_enable_host_if_generic(hw);
315 	if (ret_val)
316 		return ret_val;
317 
318 	/* Populate the host interface with the contents of "buffer". */
319 	ret_val = e1000_mng_host_if_write_generic(hw, buffer, length,
320 						  sizeof(hdr), &(hdr.checksum));
321 	if (ret_val)
322 		return ret_val;
323 
324 	/* Write the manageability command header */
325 	ret_val = e1000_mng_write_cmd_header_generic(hw, &hdr);
326 	if (ret_val)
327 		return ret_val;
328 
329 	/* Tell the ARC a new command is pending. */
330 	hicr = E1000_READ_REG(hw, E1000_HICR);
331 	E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C);
332 
333 	return E1000_SUCCESS;
334 }
335 
336 /**
337  *  e1000_enable_mng_pass_thru - Check if management passthrough is needed
338  *  @hw: pointer to the HW structure
339  *
340  *  Verifies the hardware needs to leave interface enabled so that frames can
341  *  be directed to and from the management interface.
342  **/
343 bool e1000_enable_mng_pass_thru(struct e1000_hw *hw)
344 {
345 	u32 manc;
346 	u32 fwsm, factps;
347 
348 	DEBUGFUNC("e1000_enable_mng_pass_thru");
349 
350 	if (!hw->mac.asf_firmware_present)
351 		return false;
352 
353 	manc = E1000_READ_REG(hw, E1000_MANC);
354 
355 	if (!(manc & E1000_MANC_RCV_TCO_EN))
356 		return false;
357 
358 	if (hw->mac.has_fwsm) {
359 		fwsm = E1000_READ_REG(hw, E1000_FWSM);
360 		factps = E1000_READ_REG(hw, E1000_FACTPS);
361 
362 		if (!(factps & E1000_FACTPS_MNGCG) &&
363 		    ((fwsm & E1000_FWSM_MODE_MASK) ==
364 		     (e1000_mng_mode_pt << E1000_FWSM_MODE_SHIFT)))
365 			return true;
366 	} else if ((hw->mac.type == e1000_82574) ||
367 		   (hw->mac.type == e1000_82583)) {
368 		u16 data;
369 		s32 ret_val;
370 
371 		factps = E1000_READ_REG(hw, E1000_FACTPS);
372 		ret_val = e1000_read_nvm(hw, NVM_INIT_CONTROL2_REG, 1, &data);
373 		if (ret_val)
374 			return false;
375 
376 		if (!(factps & E1000_FACTPS_MNGCG) &&
377 		    ((data & E1000_NVM_INIT_CTRL2_MNGM) ==
378 		     (e1000_mng_mode_pt << 13)))
379 			return true;
380 	} else if ((manc & E1000_MANC_SMBUS_EN) &&
381 		   !(manc & E1000_MANC_ASF_EN)) {
382 		return true;
383 	}
384 
385 	return false;
386 }
387 
388 /**
389  *  e1000_host_interface_command - Writes buffer to host interface
390  *  @hw: pointer to the HW structure
391  *  @buffer: contains a command to write
392  *  @length: the byte length of the buffer, must be multiple of 4 bytes
393  *
394  *  Writes a buffer to the Host Interface.  Upon success, returns E1000_SUCCESS
395  *  else returns E1000_ERR_HOST_INTERFACE_COMMAND.
396  **/
397 s32 e1000_host_interface_command(struct e1000_hw *hw, u8 *buffer, u32 length)
398 {
399 	u32 hicr, i;
400 
401 	DEBUGFUNC("e1000_host_interface_command");
402 
403 	if (!(hw->mac.arc_subsystem_valid)) {
404 		DEBUGOUT("Hardware doesn't support host interface command.\n");
405 		return E1000_SUCCESS;
406 	}
407 
408 	if (!hw->mac.asf_firmware_present) {
409 		DEBUGOUT("Firmware is not present.\n");
410 		return E1000_SUCCESS;
411 	}
412 
413 	if (length == 0 || length & 0x3 ||
414 	    length > E1000_HI_MAX_BLOCK_BYTE_LENGTH) {
415 		DEBUGOUT("Buffer length failure.\n");
416 		return -E1000_ERR_HOST_INTERFACE_COMMAND;
417 	}
418 
419 	/* Check that the host interface is enabled. */
420 	hicr = E1000_READ_REG(hw, E1000_HICR);
421 	if (!(hicr & E1000_HICR_EN)) {
422 		DEBUGOUT("E1000_HOST_EN bit disabled.\n");
423 		return -E1000_ERR_HOST_INTERFACE_COMMAND;
424 	}
425 
426 	/* Calculate length in DWORDs */
427 	length >>= 2;
428 
429 	/* The device driver writes the relevant command block
430 	 * into the ram area.
431 	 */
432 	for (i = 0; i < length; i++)
433 		E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF, i,
434 					    *((u32 *)buffer + i));
435 
436 	/* Setting this bit tells the ARC that a new command is pending. */
437 	E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C);
438 
439 	for (i = 0; i < E1000_HI_COMMAND_TIMEOUT; i++) {
440 		hicr = E1000_READ_REG(hw, E1000_HICR);
441 		if (!(hicr & E1000_HICR_C))
442 			break;
443 		msec_delay(1);
444 	}
445 
446 	/* Check command successful completion. */
447 	if (i == E1000_HI_COMMAND_TIMEOUT ||
448 	    (!(E1000_READ_REG(hw, E1000_HICR) & E1000_HICR_SV))) {
449 		DEBUGOUT("Command has failed with no status valid.\n");
450 		return -E1000_ERR_HOST_INTERFACE_COMMAND;
451 	}
452 
453 	for (i = 0; i < length; i++)
454 		*((u32 *)buffer + i) = E1000_READ_REG_ARRAY_DWORD(hw,
455 								  E1000_HOST_IF,
456 								  i);
457 
458 	return E1000_SUCCESS;
459 }
460 
461 /**
462  *  e1000_load_firmware - Writes proxy FW code buffer to host interface
463  *                        and execute.
464  *  @hw: pointer to the HW structure
465  *  @buffer: contains a firmware to write
466  *  @length: the byte length of the buffer, must be multiple of 4 bytes
467  *
468  *  Upon success returns E1000_SUCCESS, returns E1000_ERR_CONFIG if not enabled
469  *  in HW else returns E1000_ERR_HOST_INTERFACE_COMMAND.
470  **/
471 s32 e1000_load_firmware(struct e1000_hw *hw, u8 *buffer, u32 length)
472 {
473 	u32 hicr, hibba, fwsm, icr, i;
474 
475 	DEBUGFUNC("e1000_load_firmware");
476 
477 	if (hw->mac.type < e1000_i210) {
478 		DEBUGOUT("Hardware doesn't support loading FW by the driver\n");
479 		return -E1000_ERR_CONFIG;
480 	}
481 
482 	/* Check that the host interface is enabled. */
483 	hicr = E1000_READ_REG(hw, E1000_HICR);
484 	if (!(hicr & E1000_HICR_EN)) {
485 		DEBUGOUT("E1000_HOST_EN bit disabled.\n");
486 		return -E1000_ERR_CONFIG;
487 	}
488 	if (!(hicr & E1000_HICR_MEMORY_BASE_EN)) {
489 		DEBUGOUT("E1000_HICR_MEMORY_BASE_EN bit disabled.\n");
490 		return -E1000_ERR_CONFIG;
491 	}
492 
493 	if (length == 0 || length & 0x3 || length > E1000_HI_FW_MAX_LENGTH) {
494 		DEBUGOUT("Buffer length failure.\n");
495 		return -E1000_ERR_INVALID_ARGUMENT;
496 	}
497 
498 	/* Clear notification from ROM-FW by reading ICR register */
499 	icr = E1000_READ_REG(hw, E1000_ICR_V2);
500 
501 	/* Reset ROM-FW */
502 	hicr = E1000_READ_REG(hw, E1000_HICR);
503 	hicr |= E1000_HICR_FW_RESET_ENABLE;
504 	E1000_WRITE_REG(hw, E1000_HICR, hicr);
505 	hicr |= E1000_HICR_FW_RESET;
506 	E1000_WRITE_REG(hw, E1000_HICR, hicr);
507 	E1000_WRITE_FLUSH(hw);
508 
509 	/* Wait till MAC notifies about its readiness after ROM-FW reset */
510 	for (i = 0; i < (E1000_HI_COMMAND_TIMEOUT * 2); i++) {
511 		icr = E1000_READ_REG(hw, E1000_ICR_V2);
512 		if (icr & E1000_ICR_MNG)
513 			break;
514 		msec_delay(1);
515 	}
516 
517 	/* Check for timeout */
518 	if (i == E1000_HI_COMMAND_TIMEOUT) {
519 		DEBUGOUT("FW reset failed.\n");
520 		return -E1000_ERR_HOST_INTERFACE_COMMAND;
521 	}
522 
523 	/* Wait till MAC is ready to accept new FW code */
524 	for (i = 0; i < E1000_HI_COMMAND_TIMEOUT; i++) {
525 		fwsm = E1000_READ_REG(hw, E1000_FWSM);
526 		if ((fwsm & E1000_FWSM_FW_VALID) &&
527 		    ((fwsm & E1000_FWSM_MODE_MASK) >> E1000_FWSM_MODE_SHIFT ==
528 		    E1000_FWSM_HI_EN_ONLY_MODE))
529 			break;
530 		msec_delay(1);
531 	}
532 
533 	/* Check for timeout */
534 	if (i == E1000_HI_COMMAND_TIMEOUT) {
535 		DEBUGOUT("FW reset failed.\n");
536 		return -E1000_ERR_HOST_INTERFACE_COMMAND;
537 	}
538 
539 	/* Calculate length in DWORDs */
540 	length >>= 2;
541 
542 	/* The device driver writes the relevant FW code block
543 	 * into the ram area in DWORDs via 1kB ram addressing window.
544 	 */
545 	for (i = 0; i < length; i++) {
546 		if (!(i % E1000_HI_FW_BLOCK_DWORD_LENGTH)) {
547 			/* Point to correct 1kB ram window */
548 			hibba = E1000_HI_FW_BASE_ADDRESS +
549 				((E1000_HI_FW_BLOCK_DWORD_LENGTH << 2) *
550 				(i / E1000_HI_FW_BLOCK_DWORD_LENGTH));
551 
552 			E1000_WRITE_REG(hw, E1000_HIBBA, hibba);
553 		}
554 
555 		E1000_WRITE_REG_ARRAY_DWORD(hw, E1000_HOST_IF,
556 					    i % E1000_HI_FW_BLOCK_DWORD_LENGTH,
557 					    *((u32 *)buffer + i));
558 	}
559 
560 	/* Setting this bit tells the ARC that a new FW is ready to execute. */
561 	hicr = E1000_READ_REG(hw, E1000_HICR);
562 	E1000_WRITE_REG(hw, E1000_HICR, hicr | E1000_HICR_C);
563 
564 	for (i = 0; i < E1000_HI_COMMAND_TIMEOUT; i++) {
565 		hicr = E1000_READ_REG(hw, E1000_HICR);
566 		if (!(hicr & E1000_HICR_C))
567 			break;
568 		msec_delay(1);
569 	}
570 
571 	/* Check for successful FW start. */
572 	if (i == E1000_HI_COMMAND_TIMEOUT) {
573 		DEBUGOUT("New FW did not start within timeout period.\n");
574 		return -E1000_ERR_HOST_INTERFACE_COMMAND;
575 	}
576 
577 	return E1000_SUCCESS;
578 }
579