1/*
2 * NVRAM variable manipulation (direct mapped flash)
3 *
4 * Copyright (C) 2015, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: nvram_rw.c 258972 2011-05-11 08:57:26Z $
19 */
20
21#include <bcm_cfg.h>
22#include <typedefs.h>
23#include <bcmdefs.h>
24#include <osl.h>
25#include <bcmutils.h>
26#include <siutils.h>
27#include <bcmnvram.h>
28#include <bcmendian.h>
29#include <flashutl.h>
30#include <hndsoc.h>
31#include <sbchipc.h>
32#include <LzmaDec.h>
33#ifdef NFLASH_SUPPORT
34#include <hndnand.h>
35#endif	/* NFLASH_SUPPORT */
36#ifdef _CFE_
37#include <hndsflash.h>
38#else
39
40#endif
41
42struct nvram_tuple *_nvram_realloc(struct nvram_tuple *t, const char *name, const char *value);
43void  _nvram_free(struct nvram_tuple *t);
44int  _nvram_read(void *buf, int idx);
45
46extern char *_nvram_get(const char *name);
47extern int _nvram_set(const char *name, const char *value);
48extern int _nvram_unset(const char *name);
49extern int _nvram_getall(char *buf, int count);
50extern int _nvram_commit(struct nvram_header *header);
51extern int _nvram_init(void *si, int idx);
52extern void _nvram_exit(void);
53
54static struct nvram_header *nvram_header = NULL;
55static int nvram_do_reset = FALSE;
56
57#if defined(_CFE_) && defined(BCM_DEVINFO)
58int _nvram_hash_sync(void);
59
60char *devinfo_flashdrv_nvram = "flash0.devinfo";
61
62static struct nvram_header *devinfo_nvram_header = NULL;
63static unsigned char devinfo_nvram_nvh[MAX_NVRAM_SPACE];
64#endif /* _CFE_ && BCM_DEVINFO */
65
66#ifdef _CFE_
67/* For NAND boot, flash0.nvram will be changed to nflash0.nvram */
68char *flashdrv_nvram = "flash0.nvram";
69#endif
70
71#if defined(__ECOS)
72extern int kernel_initial;
73#define NVRAM_LOCK()	cyg_scheduler_lock()
74#define NVRAM_UNLOCK()	cyg_scheduler_unlock()
75#else
76#define NVRAM_LOCK()	do {} while (0)
77#define NVRAM_UNLOCK()	do {} while (0)
78#endif
79
80/* Convenience */
81#define KB * 1024
82#define MB * 1024 * 1024
83
84char *
85nvram_get(const char *name)
86{
87	char *value;
88
89#ifdef __ECOS
90	if (!kernel_initial)
91		return NULL;
92#endif
93
94	NVRAM_LOCK();
95	value = _nvram_get(name);
96	NVRAM_UNLOCK();
97
98	return value;
99}
100
101int
102nvram_getall(char *buf, int count)
103{
104	int ret;
105
106	NVRAM_LOCK();
107	ret = _nvram_getall(buf, count);
108	NVRAM_UNLOCK();
109
110	return ret;
111}
112
113int
114BCMINITFN(nvram_set)(const char *name, const char *value)
115{
116	int ret;
117
118	NVRAM_LOCK();
119	ret = _nvram_set(name, value);
120	NVRAM_UNLOCK();
121
122	return ret;
123}
124
125int
126BCMINITFN(nvram_unset)(const char *name)
127{
128	int ret;
129
130	NVRAM_LOCK();
131	ret = _nvram_unset(name);
132	NVRAM_UNLOCK();
133
134	return ret;
135}
136
137int
138BCMINITFN(nvram_resetgpio_init)(void *si)
139{
140	char *value;
141	int gpio;
142	si_t *sih;
143
144	sih = (si_t *)si;
145
146	value = nvram_get("reset_gpio");
147	if (!value)
148		return -1;
149
150	gpio = (int) bcm_atoi(value);
151	if (gpio > 31)
152		return -1;
153
154	/* Setup GPIO input */
155	si_gpioouten(sih, ((uint32) 1 << gpio), 0, GPIO_DRV_PRIORITY);
156
157	return gpio;
158}
159
160int
161BCMINITFN(nvram_reset)(void  *si)
162{
163	int gpio;
164	uint msec;
165	si_t * sih = (si_t *)si;
166
167	if ((gpio = nvram_resetgpio_init((void *)sih)) < 0)
168		return FALSE;
169
170	/* GPIO reset is asserted low */
171	for (msec = 0; msec < 5000; msec++) {
172		if (si_gpioin(sih) & ((uint32) 1 << gpio))
173			return FALSE;
174		OSL_DELAY(1000);
175	}
176
177	nvram_do_reset = TRUE;
178	return TRUE;
179}
180
181#ifdef NFLASH_SUPPORT
182static unsigned char nand_nvh[MAX_NVRAM_SPACE];
183
184static struct nvram_header *
185BCMINITFN(nand_find_nvram)(hndnand_t *nfl, uint32 off)
186{
187	int blocksize = nfl->blocksize;
188	unsigned char *buf = nand_nvh;
189	int rlen = sizeof(nand_nvh);
190	int len;
191
192	for (; off < nfl_boot_size(nfl); off += blocksize) {
193		if (hndnand_checkbadb(nfl, off) != 0)
194			continue;
195
196		len = blocksize;
197		if (len >= rlen)
198			len = rlen;
199
200		if (hndnand_read(nfl, off, len, buf) == 0)
201			break;
202
203		buf += len;
204		rlen -= len;
205		if (rlen == 0)
206			return (struct nvram_header *)nand_nvh;
207	}
208
209	return NULL;
210}
211#endif /* NFLASH_SUPPORT */
212
213extern unsigned char embedded_nvram[];
214
215static struct nvram_header *
216BCMINITFN(find_nvram)(si_t *sih, bool embonly, bool *isemb)
217{
218	struct nvram_header *nvh;
219	uint32 off, lim = SI_FLASH2_SZ;
220	uint32 flbase = SI_FLASH2;
221	int bootdev;
222#ifdef NFLASH_SUPPORT
223	hndnand_t *nfl_info = NULL;
224#endif
225#ifdef _CFE_
226	hndsflash_t *sfl_info = NULL;
227#endif
228
229	bootdev = soc_boot_dev((void *)sih);
230#ifdef NFLASH_SUPPORT
231	if (bootdev == SOC_BOOTDEV_NANDFLASH) {
232		/* Init nand anyway */
233		nfl_info = hndnand_init(sih);
234		if (nfl_info)
235			flbase = nfl_info->phybase;
236	}
237	else
238#endif /* NFLASH_SUPPORT */
239	if (bootdev == SOC_BOOTDEV_SFLASH) {
240#ifdef _CFE_
241		/* Init nand anyway */
242		sfl_info = hndsflash_init(sih);
243		if (sfl_info) {
244			flbase = sfl_info->phybase;
245			lim = sfl_info->size;
246		}
247#else
248	if (sih->ccrev == 42)
249		flbase = SI_NS_NORFLASH;
250#endif
251	}
252
253	if (!embonly) {
254		*isemb = FALSE;
255#ifdef NFLASH_SUPPORT
256		if (nfl_info) {
257			uint32 blocksize;
258
259			blocksize = nfl_info->blocksize;
260			off = blocksize;
261			for (; off < nfl_boot_size(nfl_info); off += blocksize) {
262				if (hndnand_checkbadb(nfl_info, off) != 0)
263					continue;
264				nvh = (struct nvram_header *)OSL_UNCACHED(flbase + off);
265				if (nvh->magic != NVRAM_MAGIC)
266					continue;
267
268				/* Read into the nand_nvram */
269				if ((nvh = nand_find_nvram(nfl_info, off)) == NULL)
270					continue;
271				if (nvram_calc_crc(nvh) == (uint8)nvh->crc_ver_init)
272					return nvh;
273			}
274		}
275		else
276#endif /* NFLASH_SUPPORT */
277		{
278			off = FLASH_MIN;
279			while (off <= lim) {
280				nvh = (struct nvram_header *)
281					OSL_UNCACHED(flbase + off - MAX_NVRAM_SPACE);
282				if (nvh->magic == NVRAM_MAGIC) {
283					if (nvram_calc_crc(nvh) == (uint8) nvh->crc_ver_init) {
284						return (nvh);
285					}
286				}
287				off <<= 1;
288			}
289		}
290#ifdef BCMDBG
291		printf("find_nvram: nvram not found, trying embedded nvram next\n");
292#endif /* BCMDBG */
293	}
294
295	/*
296	 * Provide feedback to user when nvram corruption detected.
297	 * Must be non-BCMDBG for customer release.
298	 */
299	printf("Corrupt NVRAM found, trying embedded NVRAM next.\n");
300
301	/* Now check embedded nvram */
302	*isemb = TRUE;
303	nvh = (struct nvram_header *)OSL_UNCACHED(flbase + (4 * 1024));
304	if (nvh->magic == NVRAM_MAGIC)
305		return (nvh);
306	nvh = (struct nvram_header *)OSL_UNCACHED(flbase + 1024);
307	if (nvh->magic == NVRAM_MAGIC)
308		return (nvh);
309#ifdef _CFE_
310	nvh = (struct nvram_header *)embedded_nvram;
311	if (nvh->magic == NVRAM_MAGIC)
312		return (nvh);
313#endif
314	printf("find_nvram: no nvram found\n");
315	return (NULL);
316}
317
318int
319BCMATTACHFN(nvram_init)(void *si)
320{
321	bool isemb;
322	int ret;
323	si_t *sih;
324	static int nvram_status = -1;
325
326#ifdef __ECOS
327	if (!kernel_initial)
328		return 0;
329#endif
330
331	/* Check for previous 'restore defaults' condition */
332	if (nvram_status == 1)
333		return 1;
334
335	/* Check whether nvram already initilized */
336	if (nvram_status == 0 && !nvram_do_reset)
337		return 0;
338
339	sih = (si_t *)si;
340
341	/* Restore defaults from embedded NVRAM if button held down */
342	if (nvram_do_reset) {
343		/* Initialize with embedded NVRAM */
344		nvram_header = find_nvram(sih, TRUE, &isemb);
345		ret = _nvram_init(si, 0);
346		if (ret == 0) {
347			nvram_status = 1;
348			return 1;
349		}
350		nvram_status = -1;
351		_nvram_exit();
352	}
353
354	/* Find NVRAM */
355	nvram_header = find_nvram(sih, FALSE, &isemb);
356	ret = _nvram_init(si, 0);
357	if (ret == 0) {
358		/* Restore defaults if embedded NVRAM used */
359		if (nvram_header && isemb) {
360			ret = 1;
361		}
362	}
363	nvram_status = ret;
364	return ret;
365}
366
367int
368BCMINITFN(nvram_append)(void *si, char *vars, uint varsz)
369{
370	return 0;
371}
372
373void
374BCMINITFN(nvram_exit)(void *si)
375{
376	si_t *sih;
377
378	sih = (si_t *)si;
379
380	_nvram_exit();
381}
382
383/* LZMA need to be able to allocate memory,
384 * so set it up to use the OSL memory routines,
385 * only the linux debug osl uses the osh on malloc and the osh and size on
386 * free, and the debug code checks if they are valid, so pass NULL as the osh
387 * to tell the OSL that we don't have a valid osh
388 */
389static void *SzAlloc(void *p, size_t size) { p = p; return MALLOC(NULL, size); }
390static void SzFree(void *p, void *address) { p = p; MFREE(NULL, address, 0); }
391static ISzAlloc g_Alloc = { SzAlloc, SzFree };
392
393int
394BCMINITFN(_nvram_read)(void *buf, int idx)
395{
396	uint32 *src, *dst;
397	uint i;
398
399	if (!nvram_header)
400		return -19; /* -ENODEV */
401
402#if defined(_CFE_) && defined(BCM_DEVINFO)
403	if ((!devinfo_nvram_header) && (idx == 1)) {
404		return -19; /* -ENODEV */
405	}
406
407	src = idx == 0 ? (uint32 *) nvram_header : (uint32 *) devinfo_nvram_nvh;
408#else
409	src = (uint32 *) nvram_header;
410#endif /* _CFE_ && BCM_DEVINFO */
411
412	dst = (uint32 *) buf;
413
414	for (i = 0; i < sizeof(struct nvram_header); i += 4)
415		*dst++ = *src++;
416
417	/* Since we know what the first 3 bytes of the lzma properties
418	 * should be based on what we used to compress, check them
419	 * to see if we need to decompress (uncompressed this would show up a
420	 * a single [ and then the end of nvram marker so its invalid in an
421	 * uncompressed nvram block
422	 */
423	if ((((unsigned char *)src)[0] == 0x5d) &&
424	    (((unsigned char *)src)[1] == 0) &&
425	    (((unsigned char *)src)[2] == 0)) {
426		unsigned int dstlen = nvram_header->len;
427		unsigned int srclen = MAX_NVRAM_SPACE-LZMA_PROPS_SIZE-NVRAM_HEADER_SIZE;
428		unsigned char *cp = (unsigned char *)src;
429		CLzmaDec state;
430		SRes res;
431		ELzmaStatus status;
432
433		LzmaDec_Construct(&state);
434		res = LzmaDec_Allocate(&state, cp, LZMA_PROPS_SIZE, &g_Alloc);
435		if (res != SZ_OK) {
436			printf("Error Initializing LZMA Library\n");
437			return -19;
438		}
439		LzmaDec_Init(&state);
440		res = LzmaDec_DecodeToBuf(&state,
441		                          (unsigned char *)dst, &dstlen,
442		                          &cp[LZMA_PROPS_SIZE], &srclen,
443		                          LZMA_FINISH_ANY,
444		                          &status);
445
446		LzmaDec_Free(&state, &g_Alloc);
447		if (res != SZ_OK) {
448			printf("Error Decompressing eNVRAM\n");
449			return -19;
450		}
451	} else {
452		for (; i < nvram_header->len && i < MAX_NVRAM_SPACE; i += 4)
453			*dst++ = ltoh32(*src++);
454	}
455	return 0;
456}
457
458struct nvram_tuple *
459BCMINITFN(_nvram_realloc)(struct nvram_tuple *t, const char *name, const char *value)
460{
461	if (!(t = MALLOC(NULL, sizeof(struct nvram_tuple) + strlen(name) + 1 +
462	                 strlen(value) + 1))) {
463		printf("_nvram_realloc: our of memory\n");
464		return NULL;
465	}
466
467	/* Copy name */
468	t->name = (char *) &t[1];
469	strcpy(t->name, name);
470
471	/* Copy value */
472	t->value = t->name + strlen(name) + 1;
473	strcpy(t->value, value);
474
475	return t;
476}
477
478void
479BCMINITFN(_nvram_free)(struct nvram_tuple *t)
480{
481	if (t)
482		MFREE(NULL, t, sizeof(struct nvram_tuple) + strlen(t->name) + 1 +
483		      strlen(t->value) + 1);
484}
485
486#ifdef __ECOS
487int
488BCMINITFN(nvram_reinit_hash)(void)
489{
490	struct nvram_header *header;
491	int ret;
492
493	if (!(header = (struct nvram_header *) MALLOC(NULL, MAX_NVRAM_SPACE))) {
494		printf("nvram_reinit_hash: out of memory\n");
495		return -12; /* -ENOMEM */
496	}
497
498	NVRAM_LOCK();
499
500	/* Regenerate NVRAM */
501	ret = _nvram_commit(header);
502
503	NVRAM_UNLOCK();
504	MFREE(NULL, header, MAX_NVRAM_SPACE);
505	return ret;
506}
507#endif /* __ECOS */
508
509int
510BCMINITFN(nvram_commit_internal)(bool nvram_corrupt)
511{
512	struct nvram_header *header;
513	int ret;
514	uint32 *src, *dst;
515	uint i;
516
517	if (!(header = (struct nvram_header *) MALLOC(NULL, MAX_NVRAM_SPACE))) {
518		printf("nvram_commit: out of memory\n");
519		return -12; /* -ENOMEM */
520	}
521
522	NVRAM_LOCK();
523
524	/* Regenerate NVRAM */
525	ret = _nvram_commit(header);
526	if (ret)
527		goto done;
528
529	src = (uint32 *) &header[1];
530	dst = src;
531
532	for (i = sizeof(struct nvram_header); i < header->len && i < MAX_NVRAM_SPACE; i += 4)
533		*dst++ = htol32(*src++);
534
535#ifdef _CFE_
536	if ((ret = cfe_open(flashdrv_nvram)) >= 0) {
537		if (nvram_corrupt) {
538			printf("Corrupting NVRAM...\n");
539			header->magic = NVRAM_INVALID_MAGIC;
540		}
541		cfe_writeblk(ret, 0, (unsigned char *) header, header->len);
542		cfe_close(ret);
543	}
544#else
545	if (sysFlashInit(NULL) == 0) {
546		/* set/write invalid MAGIC # (in case writing image fails/is interrupted)
547		 * write the NVRAM image to flash(with invalid magic)
548		 * set/write valid MAGIC #
549		 */
550		header->magic = NVRAM_CLEAR_MAGIC;
551		nvWriteChars((unsigned char *)&header->magic, sizeof(header->magic));
552
553		header->magic = NVRAM_INVALID_MAGIC;
554		nvWrite((unsigned short *) header, MAX_NVRAM_SPACE);
555
556		header->magic = NVRAM_MAGIC;
557		nvWriteChars((unsigned char *)&header->magic, sizeof(header->magic));
558	}
559#endif /* ifdef _CFE_ */
560
561done:
562	NVRAM_UNLOCK();
563	MFREE(NULL, header, MAX_NVRAM_SPACE);
564	return ret;
565}
566
567int
568BCMINITFN(nvram_commit)(void)
569{
570	/* do not corrupt nvram */
571	return nvram_commit_internal(FALSE);
572}
573
574#if defined(_CFE_) && defined(BCM_DEVINFO)
575static struct nvram_header *
576BCMINITFN(find_devinfo_nvram)(si_t *sih)
577{
578	int cfe_fd, ret;
579
580	if (devinfo_nvram_header != NULL) {
581		return (devinfo_nvram_header);
582	}
583
584	if ((cfe_fd = cfe_open(devinfo_flashdrv_nvram)) < 0) {
585		return NULL;
586	}
587
588	ret = cfe_read(cfe_fd, (unsigned char *)devinfo_nvram_nvh,  NVRAM_SPACE);
589	if (ret >= 0) {
590		devinfo_nvram_header = (struct nvram_header *) devinfo_nvram_nvh;
591	}
592
593	cfe_close(cfe_fd);
594
595	return (devinfo_nvram_header);
596}
597
598int
599BCMINITFN(devinfo_nvram_init)(void *si)
600{
601	int ret;
602	si_t *sih = (si_t *)si;
603
604	nvram_header = find_devinfo_nvram(sih);
605	_nvram_hash_select(1);
606	ret =  _nvram_init(si, 1);
607	_nvram_hash_select(0);
608
609	return (ret);
610}
611
612/* sync nvram hash table with devinfo nvram hash table, and commit nvram */
613int
614BCMINITFN(devinfo_nvram_sync)(void)
615{
616	_nvram_hash_sync();
617	nvram_commit();
618
619	return (0);
620}
621#endif /* _CFE_ && BCM_DEVINFO */
622