|
|
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.