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