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