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