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