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