xref: /freebsd/sys/dev/cxgbe/cudbg/cudbg_flash_utils.c (revision 3806950135d2c8633ec0764e8807eacc87cf3e10)
1 /*-
2  * Copyright (c) 2017 Chelsio Communications, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/types.h>
31 #include <sys/param.h>
32 
33 #include "common/common.h"
34 #include "common/t4_regs.h"
35 #include "cudbg.h"
36 #include "cudbg_lib_common.h"
37 
38 enum {
39 	SF_ATTEMPTS = 10,		/* max retries for SF operations */
40 
41 	/* flash command opcodes */
42 	SF_PROG_PAGE	= 2,	/* program page */
43 	SF_WR_DISABLE	= 4,	/* disable writes */
44 	SF_RD_STATUS	= 5,	/* read status register */
45 	SF_WR_ENABLE	= 6,	/* enable writes */
46 	SF_RD_DATA_FAST = 0xb,	/* read flash */
47 	SF_RD_ID	= 0x9f, /* read ID */
48 	SF_ERASE_SECTOR = 0xd8, /* erase sector */
49 };
50 
51 int write_flash(struct adapter *adap, u32 start_sec, void *data, u32 size);
52 int read_flash(struct adapter *adap, u32 start_sec , void *data, u32 size,
53 		u32 start_address);
54 
55 void
56 update_skip_size(struct cudbg_flash_sec_info *sec_info, u32 size)
57 {
58 	sec_info->skip_size += size;
59 }
60 
61 static
62 void set_sector_availability(struct cudbg_flash_sec_info *sec_info,
63     int sector_nu, int avail)
64 {
65 	sector_nu -= CUDBG_START_SEC;
66 	if (avail)
67 		set_dbg_bitmap(sec_info->sec_bitmap, sector_nu);
68 	else
69 		reset_dbg_bitmap(sec_info->sec_bitmap, sector_nu);
70 }
71 
72 /* This function will return empty sector available for filling */
73 static int
74 find_empty_sec(struct cudbg_flash_sec_info *sec_info)
75 {
76 	int i, index, bit;
77 
78 	for (i = CUDBG_START_SEC; i < CUDBG_SF_MAX_SECTOR; i++) {
79 		index = (i - CUDBG_START_SEC) / 8;
80 		bit = (i - CUDBG_START_SEC) % 8;
81 		if (!(sec_info->sec_bitmap[index] & (1 << bit)))
82 			return i;
83 	}
84 
85 	return CUDBG_STATUS_FLASH_FULL;
86 }
87 
88 /* This function will get header initially. If header is already there
89  * then it will update that header */
90 static void update_headers(void *handle, struct cudbg_buffer *dbg_buff,
91 		    u64 timestamp, u32 cur_entity_hdr_offset,
92 		    u32 start_offset, u32 ext_size)
93 {
94 	struct cudbg_private *priv = handle;
95 	struct cudbg_flash_sec_info *sec_info = &priv->sec_info;
96 	void *sec_hdr;
97 	struct cudbg_hdr *cudbg_hdr;
98 	struct cudbg_flash_hdr *flash_hdr;
99 	struct cudbg_entity_hdr *entity_hdr;
100 	u32 hdr_offset;
101 	u32 data_hdr_size;
102 	u32 total_hdr_size;
103 	u32 sec_hdr_start_addr;
104 
105 	data_hdr_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) +
106 				sizeof(struct cudbg_hdr);
107 	total_hdr_size = data_hdr_size + sizeof(struct cudbg_flash_hdr);
108 	sec_hdr_start_addr = CUDBG_SF_SECTOR_SIZE - total_hdr_size;
109 	sec_hdr  = sec_info->sec_data + sec_hdr_start_addr;
110 
111 	flash_hdr = (struct cudbg_flash_hdr *)(sec_hdr);
112 	cudbg_hdr = (struct cudbg_hdr *)dbg_buff->data;
113 
114 	/* initially initialize flash hdr and copy all data headers and
115 	 * in next calling (else part) copy only current entity header
116 	 */
117 	if ((start_offset - sec_info->skip_size) == data_hdr_size) {
118 		flash_hdr->signature = CUDBG_FL_SIGNATURE;
119 		flash_hdr->major_ver = CUDBG_FL_MAJOR_VERSION;
120 		flash_hdr->minor_ver = CUDBG_FL_MINOR_VERSION;
121 		flash_hdr->build_ver = CUDBG_FL_BUILD_VERSION;
122 		flash_hdr->hdr_len = sizeof(struct cudbg_flash_hdr);
123 		hdr_offset =  sizeof(struct cudbg_flash_hdr);
124 
125 		memcpy((void *)((char *)sec_hdr + hdr_offset),
126 		       (void *)((char *)dbg_buff->data), data_hdr_size);
127 	} else
128 		memcpy((void *)((char *)sec_hdr +
129 			sizeof(struct cudbg_flash_hdr) +
130 			cur_entity_hdr_offset),
131 			(void *)((char *)dbg_buff->data +
132 			cur_entity_hdr_offset),
133 			sizeof(struct cudbg_entity_hdr));
134 
135 	hdr_offset = data_hdr_size + sizeof(struct cudbg_flash_hdr);
136 	flash_hdr->data_len = cudbg_hdr->data_len - sec_info->skip_size;
137 	flash_hdr->timestamp = timestamp;
138 
139 	entity_hdr = (struct cudbg_entity_hdr *)((char *)sec_hdr +
140 		      sizeof(struct cudbg_flash_hdr) +
141 		      cur_entity_hdr_offset);
142 	/* big entity like mc need to be skipped */
143 	entity_hdr->start_offset -= sec_info->skip_size;
144 
145 	cudbg_hdr = (struct cudbg_hdr *)((char *)sec_hdr +
146 			sizeof(struct cudbg_flash_hdr));
147 	cudbg_hdr->data_len = flash_hdr->data_len;
148 	flash_hdr->data_len += ext_size;
149 }
150 
151 /* Write CUDBG data into serial flash */
152 int cudbg_write_flash(void *handle, u64 timestamp, void *data,
153 		      u32 start_offset, u32 cur_entity_hdr_offset,
154 		      u32 cur_entity_size,
155 		      u32 ext_size)
156 {
157 	struct cudbg_private *priv = handle;
158 	struct cudbg_init *cudbg_init = &priv->dbg_init;
159 	struct cudbg_flash_sec_info *sec_info = &priv->sec_info;
160 	struct adapter *adap = cudbg_init->adap;
161 	struct cudbg_flash_hdr *flash_hdr = NULL;
162 	struct cudbg_buffer *dbg_buff = (struct cudbg_buffer *)data;
163 	u32 data_hdr_size;
164 	u32 total_hdr_size;
165 	u32 tmp_size;
166 	u32 sec_data_offset;
167 	u32 sec_hdr_start_addr;
168 	u32 sec_data_size;
169 	u32 space_left;
170 	int rc = 0;
171 	int sec;
172 
173 	data_hdr_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) +
174 			sizeof(struct cudbg_hdr);
175 	total_hdr_size = data_hdr_size + sizeof(struct cudbg_flash_hdr);
176 	sec_hdr_start_addr = CUDBG_SF_SECTOR_SIZE - total_hdr_size;
177 	sec_data_size = sec_hdr_start_addr;
178 
179 	cudbg_init->print("\tWriting %u bytes to flash\n", cur_entity_size);
180 
181 	/* this function will get header if sec_info->sec_data does not
182 	 * have any header and
183 	 * will update the header if it has header
184 	 */
185 	update_headers(handle, dbg_buff, timestamp,
186 		       cur_entity_hdr_offset,
187 		       start_offset, ext_size);
188 
189 	if (ext_size) {
190 		cur_entity_size += sizeof(struct cudbg_entity_hdr);
191 		start_offset = dbg_buff->offset - cur_entity_size;
192 	}
193 
194 	flash_hdr = (struct cudbg_flash_hdr *)(sec_info->sec_data +
195 			sec_hdr_start_addr);
196 
197 	if (flash_hdr->data_len > CUDBG_FLASH_SIZE) {
198 		rc = CUDBG_STATUS_FLASH_FULL;
199 		goto out;
200 	}
201 
202 	space_left = CUDBG_FLASH_SIZE - flash_hdr->data_len;
203 
204 	if (cur_entity_size > space_left) {
205 		rc = CUDBG_STATUS_FLASH_FULL;
206 		goto out;
207 	}
208 
209 	while (cur_entity_size > 0) {
210 		sec = find_empty_sec(sec_info);
211 		if (sec_info->par_sec) {
212 			sec_data_offset = sec_info->par_sec_offset;
213 			set_sector_availability(sec_info, sec_info->par_sec, 0);
214 			sec_info->par_sec = 0;
215 			sec_info->par_sec_offset = 0;
216 
217 		} else {
218 			sec_info->cur_seq_no++;
219 			flash_hdr->sec_seq_no = sec_info->cur_seq_no;
220 			sec_data_offset = 0;
221 		}
222 
223 		if (cur_entity_size + sec_data_offset > sec_data_size) {
224 			tmp_size = sec_data_size - sec_data_offset;
225 		} else {
226 			tmp_size = cur_entity_size;
227 			sec_info->par_sec = sec;
228 			sec_info->par_sec_offset = cur_entity_size +
229 						  sec_data_offset;
230 		}
231 
232 		memcpy((void *)((char *)sec_info->sec_data + sec_data_offset),
233 		       (void *)((char *)dbg_buff->data + start_offset),
234 		       tmp_size);
235 
236 		rc = write_flash(adap, sec, sec_info->sec_data,
237 				CUDBG_SF_SECTOR_SIZE);
238 		if (rc)
239 			goto out;
240 
241 		cur_entity_size -= tmp_size;
242 		set_sector_availability(sec_info, sec, 1);
243 		start_offset += tmp_size;
244 	}
245 out:
246 	return rc;
247 }
248 
249 int write_flash(struct adapter *adap, u32 start_sec, void *data, u32 size)
250 {
251 	unsigned int addr;
252 	unsigned int i, n;
253 	unsigned int sf_sec_size;
254 	int rc = 0;
255 
256 	u8 *ptr = (u8 *)data;
257 
258 	sf_sec_size = adap->params.sf_size/adap->params.sf_nsec;
259 
260 	addr =  start_sec * CUDBG_SF_SECTOR_SIZE;
261 	i = DIV_ROUND_UP(size,/* # of sectors spanned */
262 			sf_sec_size);
263 
264 	rc = t4_flash_erase_sectors(adap, start_sec,
265 		   start_sec + i - 1);
266 	/*
267 	 * If size == 0 then we're simply erasing the FLASH sectors associated
268 	 * with the on-adapter OptionROM Configuration File.
269 	 */
270 
271 	if (rc || size == 0)
272 		goto out;
273 
274 	/* this will write to the flash up to SF_PAGE_SIZE at a time */
275 	for (i = 0; i < size; i += SF_PAGE_SIZE) {
276 		if ((size - i) <  SF_PAGE_SIZE)
277 			n = size - i;
278 		else
279 			n = SF_PAGE_SIZE;
280 		rc = t4_write_flash(adap, addr, n, ptr, 0);
281 		if (rc)
282 			goto out;
283 
284 		addr += n;
285 		ptr += n;
286 	}
287 
288 	return 0;
289 out:
290 	return rc;
291 }
292 
293 int cudbg_read_flash_details(void *handle, struct cudbg_flash_hdr *data)
294 {
295 	int rc;
296 	rc = cudbg_read_flash(handle, (void *)data,
297 			      sizeof(struct cudbg_flash_hdr), 0);
298 
299 	return rc;
300 }
301 
302 int cudbg_read_flash_data(void *handle, void *buf, u32 buf_size)
303 {
304 	int rc;
305 	u32 total_hdr_size, data_header_size;
306 	void *payload = NULL;
307 	u32 payload_size = 0;
308 
309 	data_header_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) +
310 		sizeof(struct cudbg_hdr);
311 	total_hdr_size = data_header_size + sizeof(struct cudbg_flash_hdr);
312 
313 	/* Copy flash header to buffer */
314 	rc = cudbg_read_flash(handle, buf, total_hdr_size, 0);
315 	if (rc != 0)
316 		goto out;
317 	payload = (char *)buf + total_hdr_size;
318 	payload_size  = buf_size - total_hdr_size;
319 
320 	/* Reading flash data to buf */
321 	rc = cudbg_read_flash(handle, payload, payload_size, 1);
322 	if (rc != 0)
323 		goto out;
324 
325 out:
326 	return rc;
327 }
328 
329 int cudbg_read_flash(void *handle, void *data, u32 size, int data_flag)
330 {
331 	struct cudbg_private *priv = handle;
332 	struct cudbg_init *cudbg_init = &priv->dbg_init;
333 	struct cudbg_flash_sec_info *sec_info = &priv->sec_info;
334 	struct adapter *adap = cudbg_init->adap;
335 	struct cudbg_flash_hdr flash_hdr;
336 	u32 total_hdr_size;
337 	u32 data_hdr_size;
338 	u32 sec_hdr_start_addr;
339 	u32 tmp_size;
340 	u32 data_offset = 0;
341 	u32 i, j;
342 	int rc;
343 
344 	rc = t4_get_flash_params(adap);
345 	if (rc) {
346 		cudbg_init->print("\nGet flash params failed."
347 			"Try Again...readflash\n\n");
348 		return rc;
349 	}
350 
351 	data_hdr_size = CUDBG_MAX_ENTITY * sizeof(struct cudbg_entity_hdr) +
352 			sizeof(struct cudbg_hdr);
353 	total_hdr_size = data_hdr_size + sizeof(struct cudbg_flash_hdr);
354 	sec_hdr_start_addr = CUDBG_SF_SECTOR_SIZE - total_hdr_size;
355 
356 	if (!data_flag) {
357 		/* fill header */
358 		if (!sec_info->max_timestamp) {
359 			/* finding max time stamp because it may
360 			 * have older filled sector also
361 			 */
362 			memset(&flash_hdr, 0, sizeof(struct cudbg_flash_hdr));
363 			rc = read_flash(adap, CUDBG_START_SEC, &flash_hdr,
364 				sizeof(struct cudbg_flash_hdr),
365 				sec_hdr_start_addr);
366 
367 			if (flash_hdr.signature == CUDBG_FL_SIGNATURE) {
368 				sec_info->max_timestamp = flash_hdr.timestamp;
369 			} else {
370 				rc = read_flash(adap, CUDBG_START_SEC + 1,
371 					&flash_hdr,
372 					sizeof(struct cudbg_flash_hdr),
373 					sec_hdr_start_addr);
374 
375 				if (flash_hdr.signature == CUDBG_FL_SIGNATURE)
376 					sec_info->max_timestamp =
377 							flash_hdr.timestamp;
378 				else {
379 					cudbg_init->print("\n\tNo cudbg dump "\
380 							  "found in flash\n\n");
381 					return CUDBG_STATUS_NO_SIGNATURE;
382 				}
383 
384 			}
385 
386 			/* finding max sequence number because max sequenced
387 			 * sector has updated header
388 			 */
389 			for (i = CUDBG_START_SEC; i <
390 					CUDBG_SF_MAX_SECTOR; i++) {
391 				memset(&flash_hdr, 0,
392 				       sizeof(struct cudbg_flash_hdr));
393 				rc = read_flash(adap, i, &flash_hdr,
394 						sizeof(struct cudbg_flash_hdr),
395 						sec_hdr_start_addr);
396 
397 				if (flash_hdr.signature == CUDBG_FL_SIGNATURE &&
398 				    sec_info->max_timestamp ==
399 				    flash_hdr.timestamp &&
400 				    sec_info->max_seq_no <=
401 				    flash_hdr.sec_seq_no) {
402 					if (sec_info->max_seq_no ==
403 					    flash_hdr.sec_seq_no) {
404 						if (sec_info->hdr_data_len <
405 						    flash_hdr.data_len)
406 							sec_info->max_seq_sec = i;
407 					} else {
408 						sec_info->max_seq_sec = i;
409 						sec_info->hdr_data_len =
410 							flash_hdr.data_len;
411 					}
412 					sec_info->max_seq_no = flash_hdr.sec_seq_no;
413 				}
414 			}
415 		}
416 		rc = read_flash(adap, sec_info->max_seq_sec,
417 				(struct cudbg_flash_hdr *)data,
418 				size, sec_hdr_start_addr);
419 
420 		if (rc)
421 			cudbg_init->print("Read flash header failed, rc %d\n",
422 					rc);
423 
424 		return rc;
425 	}
426 
427 	/* finding sector sequence sorted */
428 	for (i = 1; i <= sec_info->max_seq_no; i++) {
429 		for (j = CUDBG_START_SEC; j < CUDBG_SF_MAX_SECTOR; j++) {
430 			memset(&flash_hdr, 0, sizeof(struct cudbg_flash_hdr));
431 			rc = read_flash(adap, j, &flash_hdr,
432 				sizeof(struct cudbg_flash_hdr),
433 				sec_hdr_start_addr);
434 
435 			if (flash_hdr.signature ==
436 					CUDBG_FL_SIGNATURE &&
437 					sec_info->max_timestamp ==
438 					flash_hdr.timestamp &&
439 					flash_hdr.sec_seq_no == i) {
440 				if (size + total_hdr_size >
441 						CUDBG_SF_SECTOR_SIZE)
442 					tmp_size = CUDBG_SF_SECTOR_SIZE -
443 						total_hdr_size;
444 				else
445 					tmp_size =  size;
446 
447 				if ((i != sec_info->max_seq_no) ||
448 				    (i == sec_info->max_seq_no &&
449 				    j == sec_info->max_seq_sec)){
450 					/* filling data buffer with sector data
451 					 * except sector header
452 					 */
453 					rc = read_flash(adap, j,
454 							(void *)((char *)data +
455 							data_offset),
456 							tmp_size, 0);
457 					data_offset += (tmp_size);
458 					size -= (tmp_size);
459 					break;
460 				}
461 			}
462 		}
463 	}
464 
465 	return rc;
466 }
467 
468 int read_flash(struct adapter *adap, u32 start_sec , void *data, u32 size,
469 		u32 start_address)
470 {
471 	unsigned int addr, i, n;
472 	int rc;
473 	u32 *ptr = (u32 *)data;
474 	addr = start_sec * CUDBG_SF_SECTOR_SIZE + start_address;
475 	size = size / 4;
476 	for (i = 0; i < size; i += SF_PAGE_SIZE) {
477 		if ((size - i) <  SF_PAGE_SIZE)
478 			n = size - i;
479 		else
480 			n = SF_PAGE_SIZE;
481 		rc = t4_read_flash(adap, addr, n, ptr, 0);
482 		if (rc)
483 			goto out;
484 
485 		addr = addr + (n*4);
486 		ptr += n;
487 	}
488 
489 	return 0;
490 out:
491 	return rc;
492 }
493