Annotation of qemu/roms/SLOF/lib/libnvram/nvram.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.