1 /******************************************************************************
2  * Copyright (c) 2004, 2008 IBM Corporation
3  * All rights reserved.
4  * This program and the accompanying materials
5  * are made available under the terms of the BSD License
6  * which accompanies this distribution, and is available at
7  * http://www.opensource.org/licenses/bsd-license.php
8  *
9  * Contributors:
10  *     IBM Corporation - initial implementation
11  *****************************************************************************/
12 
13 #include "cache.h"
14 #include "nvram.h"
15 #include "../libhvcall/libhvcall.h"
16 
17 #include <stdio.h>
18 #include <stdarg.h>
19 #include <string.h>
20 #include <southbridge.h>
21 #include <nvramlog.h>
22 #include <byteorder.h>
23 
24 #ifdef RTAS_NVRAM
25 static uint32_t fetch_token;
26 static uint32_t store_token;
27 static uint32_t NVRAM_LENGTH;
28 static char *nvram_buffer; /* use buffer allocated by SLOF code */
29 #else
30 #ifndef NVRAM_LENGTH
31 #define NVRAM_LENGTH	0x10000
32 #endif
33 /*
34  * This is extremely ugly, but still better than implementing
35  * another sbrk() around it.
36  */
37 static char nvram_buffer[NVRAM_LENGTH];
38 #endif
39 
40 static uint8_t nvram_buffer_locked=0x00;
41 
nvram_init(uint32_t _fetch_token,uint32_t _store_token,long _nvram_length,void * nvram_addr)42 void nvram_init(uint32_t _fetch_token, uint32_t _store_token,
43 		long _nvram_length, void* nvram_addr)
44 {
45 #ifdef RTAS_NVRAM
46 	fetch_token = _fetch_token;
47 	store_token = _store_token;
48 	NVRAM_LENGTH = _nvram_length;
49 	nvram_buffer = nvram_addr;
50 
51 	DEBUG("\nNVRAM: size=%d, fetch=%x, store=%x\n",
52 		NVRAM_LENGTH, fetch_token, store_token);
53 #endif
54 }
55 
56 
57 void asm_cout(long Character,long UART,long NVRAM);
58 
59 #if defined(DISABLE_NVRAM)
60 
61 static volatile uint8_t nvram[NVRAM_LENGTH]; /* FAKE */
62 
63 #define nvram_access(type,size,name) 				\
64 	type nvram_read_##name(unsigned int offset)		\
65 	{							\
66 		type *pos;					\
67 		if (offset > (NVRAM_LENGTH - sizeof(type)))	\
68 			return 0;				\
69 		pos = (type *)(nvram+offset);			\
70 		return *pos;					\
71 	}							\
72 	void nvram_write_##name(unsigned int offset, type data)	\
73 	{							\
74 		type *pos;					\
75 		if (offset > (NVRAM_LENGTH - sizeof(type)))	\
76 			return;					\
77 		pos = (type *)(nvram+offset);			\
78 		*pos = data;					\
79 	}
80 
81 #elif defined(RTAS_NVRAM)
82 
nvram_fetch(unsigned int offset,void * buf,unsigned int len)83 static inline void nvram_fetch(unsigned int offset, void *buf, unsigned int len)
84 {
85  	struct hv_rtas_call rtas = {
86 		.token = fetch_token,
87 		.nargs = 3,
88 		.nrets = 2,
89 		.argret = { offset, (uint32_t)(unsigned long)buf, len },
90 	};
91 	h_rtas(&rtas);
92 }
93 
nvram_store(unsigned int offset,void * buf,unsigned int len)94 static inline void nvram_store(unsigned int offset, void *buf, unsigned int len)
95 {
96 	struct hv_rtas_call rtas = {
97 		.token = store_token,
98 		.nargs = 3,
99 		.nrets = 2,
100 		.argret = { offset, (uint32_t)(unsigned long)buf, len },
101 	};
102 	h_rtas(&rtas);
103 }
104 
105 #define nvram_access(type,size,name) 				\
106 	type nvram_read_##name(unsigned int offset)		\
107 	{							\
108 		type val;					\
109 		if (offset > (NVRAM_LENGTH - sizeof(type)))	\
110 			return 0;				\
111 		nvram_fetch(offset, &val, size / 8);		\
112 		return val;					\
113 	}							\
114 	void nvram_write_##name(unsigned int offset, type data)	\
115 	{							\
116 		if (offset > (NVRAM_LENGTH - sizeof(type)))	\
117 			return;					\
118 		nvram_store(offset, &data, size / 8);		\
119 	}
120 
121 #else	/* DISABLE_NVRAM */
122 
123 static volatile uint8_t *nvram = (volatile uint8_t *)SB_NVRAM_adr;
124 
125 #define nvram_access(type,size,name) 				\
126 	type nvram_read_##name(unsigned int offset)		\
127 	{							\
128 		type *pos;					\
129 		if (offset > (NVRAM_LENGTH - sizeof(type)))	\
130 			return 0;				\
131 		pos = (type *)(nvram+offset);			\
132 		return ci_read_##size(pos);			\
133 	}							\
134 	void nvram_write_##name(unsigned int offset, type data)	\
135 	{							\
136 		type *pos;					\
137 		if (offset > (NVRAM_LENGTH - sizeof(type)))	\
138 			return;					\
139 		pos = (type *)(nvram+offset);			\
140 		ci_write_##size(pos, data);			\
141 	}
142 
143 #endif
144 
145 /*
146  * producer for nvram access functions. Since these functions are
147  * basically all the same except for the used data types, produce
148  * them via the nvram_access macro to keep the code from bloating.
149  */
150 
151 nvram_access(uint8_t,   8, byte)
152 nvram_access(uint16_t, 16, word)
153 nvram_access(uint32_t, 32, dword)
154 nvram_access(uint64_t, 64, qword)
155 
156 
157 
158 /**
159  * This function is a minimal abstraction for our temporary
160  * buffer. It should have been malloced, but since there is no
161  * usable malloc, we go this route.
162  *
163  * @return pointer to temporary buffer
164  */
165 
get_nvram_buffer(int len)166 char *get_nvram_buffer(int len)
167 {
168 	if(len>NVRAM_LENGTH)
169 		return NULL;
170 
171 	if(nvram_buffer_locked)
172 		return NULL;
173 
174 	nvram_buffer_locked = 0xff;
175 
176 	return nvram_buffer;
177 }
178 
179 /**
180  * @param buffer pointer to the allocated buffer. This
181  * is unused, but nice in case we ever get a real malloc
182  */
183 
free_nvram_buffer(char * buffer)184 void free_nvram_buffer(char *buffer __attribute__((unused)))
185 {
186 	nvram_buffer_locked = 0x00;
187 }
188 
189 /**
190  * @param fmt format string, like in printf
191  * @param ... variable number of arguments
192  */
193 
nvramlog_printf(const char * fmt,...)194 int nvramlog_printf(const char* fmt, ...)
195 {
196 	char buff[256];
197 	int count, i;
198 	va_list ap;
199 
200 	va_start(ap, fmt);
201 	count = vsprintf(buff, fmt, ap);
202 	va_end(ap);
203 
204 	for (i=0; i<count; i++)
205 		asm_cout(buff[i], 0, 1);
206 
207 	return count;
208 }
209 
210 /**
211  * @param offset start offset of the partition header
212  */
213 
get_partition_type(int offset)214 static uint8_t get_partition_type(int offset)
215 {
216 	return nvram_read_byte(offset);
217 }
218 
219 /**
220  * @param offset start offset of the partition header
221  */
222 
get_partition_header_checksum(int offset)223 static uint8_t get_partition_header_checksum(int offset)
224 {
225 	return nvram_read_byte(offset+1);
226 }
227 
228 /**
229  * @param offset start offset of the partition header
230  */
231 
get_partition_len(int offset)232 static uint16_t get_partition_len(int offset)
233 {
234 	return nvram_read_word(offset+2);
235 }
236 
237 /**
238  * @param offset start offset of the partition header
239  * @return static char array containing the partition name
240  *
241  * NOTE: If the partition name needs to be non-temporary, strdup
242  * and use the copy instead.
243  */
244 
get_partition_name(int offset)245 static char * get_partition_name(int offset)
246 {
247 	static char name[12];
248 	int i;
249 	for (i=0; i<12; i++)
250 		name[i]=nvram_read_byte(offset+4+i);
251 
252 	DEBUG("name: \"%s\"\n", name);
253 	return name;
254 }
255 
calc_partition_header_checksum(int offset)256 static uint8_t calc_partition_header_checksum(int offset)
257 {
258 	uint16_t plainsum;
259 	uint8_t checksum;
260 	int i;
261 
262 	plainsum = nvram_read_byte(offset);
263 
264 	for (i=2; i<PARTITION_HEADER_SIZE; i++)
265 		plainsum+=nvram_read_byte(offset+i);
266 
267 	checksum=(plainsum>>8)+(plainsum&0xff);
268 
269 	return checksum;
270 }
271 
calc_used_nvram_space(void)272 static int calc_used_nvram_space(void)
273 {
274 	int walk, len;
275 
276 	for (walk=0; walk<NVRAM_LENGTH;) {
277 		if(nvram_read_byte(walk) == 0
278 		   || get_partition_header_checksum(walk) !=
279 				calc_partition_header_checksum(walk)) {
280 			/* If there's no valid entry, bail out */
281 			break;
282 		}
283 
284 		len=get_partition_len(walk);
285 		DEBUG("... part len=%x, %x\n", len, len*16);
286 
287 		if(!len) {
288 			/* If there's a partition type but no len, bail out.
289 			 * Don't bail out if type is 0. This can be used to
290 			 * find the offset of the first free byte.
291 			 */
292 			break;
293 		}
294 
295 		walk += len * 16;
296 	}
297 	DEBUG("used nvram space: %d\n", walk);
298 
299 	return walk;
300 }
301 
302 /**
303  *
304  * @param type partition type. Set this to the partition type you are looking
305  *             for. If there are several partitions with the same type, only
306  *             the first partition with that type will be found.
307  *             Set to -1 to ignore. Set to 0 to find free unpartitioned space.
308  *
309  * @param name partition name. Set this to the name of the partition you are
310  *             looking for. If there are several partitions with the same name,
311  *             only the first partition with that name will be found.
312  *             Set to NULL to ignore.
313  *
314  * To disambiguate the partitions you should have a unique name if you plan to
315  * have several partitions of the same type.
316  *
317  */
318 
get_partition(unsigned int type,char * name)319 partition_t get_partition(unsigned int type, char *name)
320 {
321 	partition_t ret={0,-1};
322 	int walk, len;
323 
324 	DEBUG("get_partition(%i, '%s')\n", type, name);
325 
326 	for (walk=0; walk<NVRAM_LENGTH;) {
327 		// DEBUG("get_partition: walk=%x\n", walk);
328 		if(get_partition_header_checksum(walk) !=
329 				calc_partition_header_checksum(walk)) {
330 			/* If there's no valid entry, bail out */
331 			break;
332 		}
333 
334 		len=get_partition_len(walk);
335 		if(type && !len) {
336 			/* If there's a partition type but no len, bail out.
337 			 * Don't bail out if type is 0. This can be used to
338 			 * find the offset of the first free byte.
339 			 */
340 			break;
341 		}
342 
343 		/* Check if either type or name or both do not match. */
344 		if ( (type!=(unsigned int)-1 && type != get_partition_type(walk)) ||
345 			(name && strncmp(get_partition_name(walk), name, 12)) ) {
346 			/* We hit another partition. Continue
347 			 * at the end of this partition
348 			 */
349 			walk += len*16;
350 			continue;
351 		}
352 
353 		ret.addr=walk+PARTITION_HEADER_SIZE;
354 		ret.len=(len*16)-PARTITION_HEADER_SIZE;
355 		break;
356 	}
357 
358 	return ret;
359 }
360 
361 /* Get partition specified by a Forth string */
get_partition_fs(char * name,int namelen)362 partition_t get_partition_fs(char *name, int namelen)
363 {
364 	char buf[namelen + 1];
365 
366 	memcpy(buf, name, namelen);
367 	buf[namelen] = 0;
368 
369 	return get_partition(-1, buf);
370 }
371 
erase_nvram(int offset,int len)372 void erase_nvram(int offset, int len)
373 {
374 	int i;
375 
376 #ifdef RTAS_NVRAM
377 	char *erase_buf = get_nvram_buffer(len);
378 	if (erase_buf) {
379 		/* Speed up by erasing all memory at once */
380 		memset(erase_buf, 0, len);
381 		nvram_store(offset, erase_buf, len);
382 		free_nvram_buffer(erase_buf);
383 		return;
384 	}
385 	/* If get_nvram_buffer failed, fall through to default code */
386 #endif
387 	for (i=offset; i<offset+len; i++)
388 		nvram_write_byte(i, 0);
389 }
390 
wipe_nvram(void)391 void wipe_nvram(void)
392 {
393 	erase_nvram(0, NVRAM_LENGTH);
394 }
395 
396 /**
397  * @param partition   partition structure pointing to the partition to wipe.
398  * @param header_only if header_only is != 0 only the partition header is
399  *                    nulled out, not the whole partition.
400  */
401 
wipe_partition(partition_t partition,int header_only)402 int wipe_partition(partition_t partition, int header_only)
403 {
404 	int pstart, len;
405 
406 	pstart=partition.addr-PARTITION_HEADER_SIZE;
407 
408 	len=PARTITION_HEADER_SIZE;
409 
410 	if(!header_only)
411 		len += partition.len;
412 
413 	erase_nvram(pstart, len);
414 
415 	return 0;
416 }
417 
418 
create_nvram_partition(int type,const char * name,int len)419 static partition_t create_nvram_partition(int type, const char *name, int len)
420 {
421 	partition_t ret = { 0, 0 };
422 	int offset, plen;
423 	unsigned int i;
424 
425 	plen = ALIGN(len+PARTITION_HEADER_SIZE, 16);
426 
427 	DEBUG("Creating partition type=%x, name=%s, len=%d plen=%d\n",
428 			type, name, len, plen);
429 
430 	offset = calc_used_nvram_space();
431 
432 	if (NVRAM_LENGTH-(calc_used_nvram_space())<plen) {
433 		DEBUG("Not enough free space.\n");
434 		return ret;
435 	}
436 
437 	DEBUG("Writing header.");
438 
439 	nvram_write_byte(offset, type);
440 	nvram_write_word(offset+2, plen/16);
441 
442 	for (i=0; i<strlen(name); i++)
443 		nvram_write_byte(offset+4+i, name[i]);
444 
445 	nvram_write_byte(offset+1, calc_partition_header_checksum(offset));
446 
447 	ret.addr = offset+PARTITION_HEADER_SIZE;
448 	ret.len = len;
449 
450 	DEBUG("partition created: addr=%lx len=%lx\n", ret.addr, ret.len);
451 
452 	return ret;
453 }
454 
create_free_partition(void)455 static int create_free_partition(void)
456 {
457 	int free_space;
458 	partition_t free_part;
459 
460 	free_space = NVRAM_LENGTH - calc_used_nvram_space() - PARTITION_HEADER_SIZE;
461 	free_part = create_nvram_partition(0x7f, "free space", free_space);
462 
463 	return (free_part.addr != 0);
464 }
465 
new_nvram_partition(int type,char * name,int len)466 partition_t new_nvram_partition(int type, char *name, int len)
467 {
468 	partition_t free_part, new_part = { 0, 0 };
469 
470 	/* NOTE: Assume all free space is consumed by the "free space"
471 	 * partition. This means a partition can not be increased in the middle
472 	 * of reset_nvram, which is obviously not a big loss.
473 	 */
474 
475 	free_part=get_partition(0x7f, NULL);
476 	if( free_part.len && free_part.len != -1)
477 		wipe_partition(free_part, 1);
478 
479 	new_part = create_nvram_partition(type, name, len);
480 
481 	if(new_part.len != len) {
482 		new_part.len = 0;
483 		new_part.addr = 0;
484 	}
485 
486 	create_free_partition();
487 
488 	return new_part;
489 }
490 
new_nvram_partition_fs(int type,char * name,int namelen,int len)491 partition_t new_nvram_partition_fs(int type, char *name, int namelen, int len)
492 {
493 	char buf[13];
494 	int i;
495 
496 	for (i = 0; i < 12; i++) {
497 		if (i < namelen)
498 			buf[i] = name[i];
499 		else
500 			buf[i] = 0;
501 	}
502 	buf[12] = 0;
503 
504 	return new_nvram_partition(type, buf, len);
505 }
506 
507 /**
508  * @param partition   partition structure pointing to the partition to wipe.
509  */
510 
delete_nvram_partition(partition_t partition)511 int delete_nvram_partition(partition_t partition)
512 {
513 	int i;
514 	partition_t free_part;
515 
516 	if(!partition.len || partition.len == -1)
517 		return 0;
518 
519 	for (i=partition.addr+partition.len; i< NVRAM_LENGTH; i++)
520 		nvram_write_byte(i - partition.len - PARTITION_HEADER_SIZE, nvram_read_byte(i));
521 
522 	erase_nvram(NVRAM_LENGTH-partition.len-PARTITION_HEADER_SIZE,
523 			partition.len-PARTITION_HEADER_SIZE);
524 
525 	free_part=get_partition(0x7f, NULL);
526 	wipe_partition(free_part, 0);
527 	create_free_partition();
528 
529 	return 1;
530 }
531 
clear_nvram_partition(partition_t part)532 int clear_nvram_partition(partition_t part)
533 {
534 	if(!part.addr)
535 		return 0;
536 
537 	erase_nvram(part.addr, part.len);
538 
539 	return 1;
540 }
541 
542 
increase_nvram_partition_size(partition_t partition,int newsize)543 int increase_nvram_partition_size(partition_t partition, int newsize)
544 {
545 	partition_t free_part;
546 	int free_offset, end_offset, i;
547 
548 	/* We don't support shrinking partitions (yet) */
549 	if (newsize < partition.len) {
550 		return 0;
551 	}
552 
553 	/* NOTE: Assume all free space is consumed by the "free space"
554 	 * partition. This means a partition can not be increased in the middle
555 	 * of reset_nvram, which is obviously not a big loss.
556 	 */
557 
558 	free_part=get_partition(0x7f, NULL);
559 
560 	// FIXME: It could be 16 byte more. Also handle empty "free" partition.
561 	if (free_part.len == -1 || free_part.len < newsize - partition.len ) {
562 		return 0;
563 	}
564 
565 	free_offset=free_part.addr - PARTITION_HEADER_SIZE; // first unused byte
566 	end_offset=partition.addr + partition.len; // last used byte of partition + 1
567 
568 	if(free_offset > end_offset) {
569 		int j, bufferlen;
570 		char *overlap_buffer;
571 
572 		bufferlen=free_offset - end_offset;
573 
574 		overlap_buffer=get_nvram_buffer(bufferlen);
575 		if(!overlap_buffer) {
576 			return 0;
577 		}
578 
579 		for (i=end_offset, j=0; i<free_offset; i++, j++)
580 			overlap_buffer[j]=nvram_read_byte(i);
581 
582 		/* Only wipe the header. The free space partition is empty per
583 		 * definition
584 		 */
585 
586 		wipe_partition(free_part, 1);
587 
588 		for (i=partition.addr+newsize, j=0; i<(int)(partition.addr+newsize+bufferlen); i++, j++)
589 			nvram_write_byte(i, overlap_buffer[j]);
590 
591 		free_nvram_buffer(overlap_buffer);
592 	} else {
593 		/* Only wipe the header. */
594 		wipe_partition(free_part, 1);
595 	}
596 
597 	/* Clear the new partition space */
598 	erase_nvram(partition.addr+partition.len, newsize-partition.len);
599 
600 	nvram_write_word(partition.addr - 16 + 2, newsize);
601 
602 	create_free_partition();
603 
604 	return 1;
605 }
606 
init_cpulog_partition(partition_t cpulog)607 static void init_cpulog_partition(partition_t cpulog)
608 {
609 	unsigned int offset=cpulog.addr;
610 
611 	/* see board-xxx/include/nvramlog.h for information */
612 	nvram_write_word(offset+0, 0x40);  // offset
613 	nvram_write_word(offset+2, 0x00);  // flags
614 	nvram_write_dword(offset+4, 0x01); // pointer
615 
616 }
617 
reset_nvram(void)618 void reset_nvram(void)
619 {
620 	partition_t cpulog0, cpulog1;
621 	struct {
622 		uint32_t prefix;
623 		uint64_t name;
624 	} __attribute__((packed)) header;
625 
626 	DEBUG("Erasing NVRAM\n");
627 	erase_nvram(0, NVRAM_LENGTH);
628 
629 	DEBUG("Creating CPU log partitions\n");
630 	header.prefix = be32_to_cpu(LLFW_LOG_BE0_NAME_PREFIX);
631 	header.name   = be64_to_cpu(LLFW_LOG_BE0_NAME);
632 	cpulog0=create_nvram_partition(LLFW_LOG_BE0_SIGNATURE, (char *)&header,
633 			(LLFW_LOG_BE0_LENGTH*16)-PARTITION_HEADER_SIZE);
634 
635 	header.prefix = be32_to_cpu(LLFW_LOG_BE1_NAME_PREFIX);
636 	header.name   = be64_to_cpu(LLFW_LOG_BE1_NAME);
637 	cpulog1=create_nvram_partition(LLFW_LOG_BE1_SIGNATURE, (char *)&header,
638 			(LLFW_LOG_BE1_LENGTH*16)-PARTITION_HEADER_SIZE);
639 
640 	DEBUG("Initializing CPU log partitions\n");
641 	init_cpulog_partition(cpulog0);
642 	init_cpulog_partition(cpulog1);
643 
644 	nvramlog_printf("Creating common NVRAM partition\r\n");
645 	create_nvram_partition(0x70, "common", 0x01000-PARTITION_HEADER_SIZE);
646 
647 	create_free_partition();
648 }
649 
nvram_debug(void)650 void nvram_debug(void)
651 {
652 #ifndef RTAS_NVRAM
653 	printf("\nNVRAM_BASE: %p\n", nvram);
654 	printf("NVRAM_LEN: 0x%x\n", NVRAM_LENGTH);
655 #endif
656 }
657 
get_nvram_size(void)658 unsigned int get_nvram_size(void)
659 {
660 	return NVRAM_LENGTH;
661 }
662