1 /******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
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
10 * IBM Corporation - initial implementation
11 *****************************************************************************/
15 #include "../libhvcall/libhvcall.h"
20 #include <southbridge.h>
22 #include <byteorder.h>
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 */
31 #define NVRAM_LENGTH 0x10000
34 * This is extremely ugly, but still better than implementing
35 * another sbrk() around it.
37 static char nvram_buffer[NVRAM_LENGTH];
40 static uint8_t nvram_buffer_locked=0x00;
42 void nvram_init(uint32_t _fetch_token, uint32_t _store_token,
43 long _nvram_length, void* nvram_addr)
46 fetch_token = _fetch_token;
47 store_token = _store_token;
48 NVRAM_LENGTH = _nvram_length;
49 nvram_buffer = nvram_addr;
51 DEBUG("\nNVRAM: size=%d, fetch=%x, store=%x\n",
52 NVRAM_LENGTH, fetch_token, store_token);
57 void asm_cout(long Character,long UART,long NVRAM);
59 #if defined(DISABLE_NVRAM)
61 static volatile uint8_t nvram[NVRAM_LENGTH]; /* FAKE */
63 #define nvram_access(type,size,name) \
64 type nvram_read_##name(unsigned int offset) \
67 if (offset > (NVRAM_LENGTH - sizeof(type))) \
69 pos = (type *)(nvram+offset); \
72 void nvram_write_##name(unsigned int offset, type data) \
75 if (offset > (NVRAM_LENGTH - sizeof(type))) \
77 pos = (type *)(nvram+offset); \
81 #elif defined(RTAS_NVRAM)
83 static inline void nvram_fetch(unsigned int offset, void *buf, unsigned int len)
85 struct hv_rtas_call rtas = {
89 .argret = { offset, (uint32_t)(unsigned long)buf, len },
94 static inline void nvram_store(unsigned int offset, void *buf, unsigned int len)
96 struct hv_rtas_call rtas = {
100 .argret = { offset, (uint32_t)(unsigned long)buf, len },
105 #define nvram_access(type,size,name) \
106 type nvram_read_##name(unsigned int offset) \
109 if (offset > (NVRAM_LENGTH - sizeof(type))) \
111 nvram_fetch(offset, &val, size / 8); \
114 void nvram_write_##name(unsigned int offset, type data) \
116 if (offset > (NVRAM_LENGTH - sizeof(type))) \
118 nvram_store(offset, &data, size / 8); \
121 #else /* DISABLE_NVRAM */
123 static volatile uint8_t *nvram = (volatile uint8_t *)SB_NVRAM_adr;
125 #define nvram_access(type,size,name) \
126 type nvram_read_##name(unsigned int offset) \
129 if (offset > (NVRAM_LENGTH - sizeof(type))) \
131 pos = (type *)(nvram+offset); \
132 return ci_read_##size(pos); \
134 void nvram_write_##name(unsigned int offset, type data) \
137 if (offset > (NVRAM_LENGTH - sizeof(type))) \
139 pos = (type *)(nvram+offset); \
140 ci_write_##size(pos, data); \
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.
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)
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.
163 * @return pointer to temporary buffer
166 char *get_nvram_buffer(int len)
171 if(nvram_buffer_locked)
174 nvram_buffer_locked = 0xff;
180 * @param buffer pointer to the allocated buffer. This
181 * is unused, but nice in case we ever get a real malloc
184 void free_nvram_buffer(char *buffer __attribute__((unused)))
186 nvram_buffer_locked = 0x00;
190 * @param fmt format string, like in printf
191 * @param ... variable number of arguments
194 int nvramlog_printf(const char* fmt, ...)
201 count = vsprintf(buff, fmt, ap);
204 for (i=0; i<count; i++)
205 asm_cout(buff[i], 0, 1);
211 * @param offset start offset of the partition header
214 static uint8_t get_partition_type(int offset)
216 return nvram_read_byte(offset);
220 * @param offset start offset of the partition header
223 static uint8_t get_partition_header_checksum(int offset)
225 return nvram_read_byte(offset+1);
229 * @param offset start offset of the partition header
232 static uint16_t get_partition_len(int offset)
234 return nvram_read_word(offset+2);
238 * @param offset start offset of the partition header
239 * @return static char array containing the partition name
241 * NOTE: If the partition name needs to be non-temporary, strdup
242 * and use the copy instead.
245 static char * get_partition_name(int offset)
247 static char name[12];
250 name[i]=nvram_read_byte(offset+4+i);
252 DEBUG("name: \"%s\"\n", name);
256 static uint8_t calc_partition_header_checksum(int offset)
262 plainsum = nvram_read_byte(offset);
264 for (i=2; i<PARTITION_HEADER_SIZE; i++)
265 plainsum+=nvram_read_byte(offset+i);
267 checksum=(plainsum>>8)+(plainsum&0xff);
272 static int calc_used_nvram_space(void)
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 */
284 len=get_partition_len(walk);
285 DEBUG("... part len=%x, %x\n", len, len*16);
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.
297 DEBUG("used nvram space: %d\n", walk);
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.
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.
314 * To disambiguate the partitions you should have a unique name if you plan to
315 * have several partitions of the same type.
319 partition_t get_partition(unsigned int type, char *name)
321 partition_t ret={0,-1};
324 DEBUG("get_partition(%i, '%s')\n", type, name);
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 */
334 len=get_partition_len(walk);
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.
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
353 ret.addr=walk+PARTITION_HEADER_SIZE;
354 ret.len=(len*16)-PARTITION_HEADER_SIZE;
361 void erase_nvram(int offset, int len)
365 for (i=offset; i<offset+len; i++)
366 nvram_write_byte(i, 0);
369 void wipe_nvram(void)
371 erase_nvram(0, NVRAM_LENGTH);
375 * @param partition partition structure pointing to the partition to wipe.
376 * @param header_only if header_only is != 0 only the partition header is
377 * nulled out, not the whole partition.
380 int wipe_partition(partition_t partition, int header_only)
384 pstart=partition.addr-PARTITION_HEADER_SIZE;
386 len=PARTITION_HEADER_SIZE;
389 len += partition.len;
391 erase_nvram(pstart, len);
397 static partition_t create_nvram_partition(int type, const char *name, int len)
399 partition_t ret = { 0, 0 };
403 plen = ALIGN(len+PARTITION_HEADER_SIZE, 16);
405 DEBUG("Creating partition type=%x, name=%s, len=%d plen=%d\n",
406 type, name, len, plen);
408 offset = calc_used_nvram_space();
410 if (NVRAM_LENGTH-(calc_used_nvram_space())<plen) {
411 DEBUG("Not enough free space.\n");
415 DEBUG("Writing header.");
417 nvram_write_byte(offset, type);
418 nvram_write_word(offset+2, plen/16);
420 for (i=0; i<strlen(name); i++)
421 nvram_write_byte(offset+4+i, name[i]);
423 nvram_write_byte(offset+1, calc_partition_header_checksum(offset));
425 ret.addr = offset+PARTITION_HEADER_SIZE;
428 DEBUG("partition created: addr=%lx len=%lx\n", ret.addr, ret.len);
433 static int create_free_partition(void)
436 partition_t free_part;
438 free_space = NVRAM_LENGTH - calc_used_nvram_space() - PARTITION_HEADER_SIZE;
439 free_part = create_nvram_partition(0x7f, "free space", free_space);
441 return (free_part.addr != 0);
444 partition_t new_nvram_partition(int type, char *name, int len)
446 partition_t free_part, new_part = { 0, 0 };
448 /* NOTE: Assume all free space is consumed by the "free space"
449 * partition. This means a partition can not be increased in the middle
450 * of reset_nvram, which is obviously not a big loss.
453 free_part=get_partition(0x7f, NULL);
454 if( free_part.len && free_part.len != -1)
455 wipe_partition(free_part, 1);
457 new_part = create_nvram_partition(type, name, len);
459 if(new_part.len != len) {
464 create_free_partition();
470 * @param partition partition structure pointing to the partition to wipe.
473 int delete_nvram_partition(partition_t partition)
476 partition_t free_part;
478 if(!partition.len || partition.len == -1)
481 for (i=partition.addr+partition.len; i< NVRAM_LENGTH; i++)
482 nvram_write_byte(i - partition.len - PARTITION_HEADER_SIZE, nvram_read_byte(i));
484 erase_nvram(NVRAM_LENGTH-partition.len-PARTITION_HEADER_SIZE,
485 partition.len-PARTITION_HEADER_SIZE);
487 free_part=get_partition(0x7f, NULL);
488 wipe_partition(free_part, 0);
489 create_free_partition();
494 int clear_nvram_partition(partition_t part)
499 erase_nvram(part.addr, part.len);
505 int increase_nvram_partition_size(partition_t partition, int newsize)
507 partition_t free_part;
508 int free_offset, end_offset, i;
510 /* We don't support shrinking partitions (yet) */
511 if (newsize < partition.len) {
515 /* NOTE: Assume all free space is consumed by the "free space"
516 * partition. This means a partition can not be increased in the middle
517 * of reset_nvram, which is obviously not a big loss.
520 free_part=get_partition(0x7f, NULL);
522 // FIXME: It could be 16 byte more. Also handle empty "free" partition.
523 if (free_part.len == -1 || free_part.len < newsize - partition.len ) {
527 free_offset=free_part.addr - PARTITION_HEADER_SIZE; // first unused byte
528 end_offset=partition.addr + partition.len; // last used byte of partition + 1
530 if(free_offset > end_offset) {
532 char *overlap_buffer;
534 bufferlen=free_offset - end_offset;
536 overlap_buffer=get_nvram_buffer(bufferlen);
537 if(!overlap_buffer) {
541 for (i=end_offset, j=0; i<free_offset; i++, j++)
542 overlap_buffer[j]=nvram_read_byte(i);
544 /* Only wipe the header. The free space partition is empty per
548 wipe_partition(free_part, 1);
550 for (i=partition.addr+newsize, j=0; i<(int)(partition.addr+newsize+bufferlen); i++, j++)
551 nvram_write_byte(i, overlap_buffer[j]);
553 free_nvram_buffer(overlap_buffer);
555 /* Only wipe the header. */
556 wipe_partition(free_part, 1);
559 /* Clear the new partition space */
560 erase_nvram(partition.addr+partition.len, newsize-partition.len);
562 nvram_write_word(partition.addr - 16 + 2, newsize);
564 create_free_partition();
569 static void init_cpulog_partition(partition_t cpulog)
571 unsigned int offset=cpulog.addr;
573 /* see board-xxx/include/nvramlog.h for information */
574 nvram_write_word(offset+0, 0x40); // offset
575 nvram_write_word(offset+2, 0x00); // flags
576 nvram_write_dword(offset+4, 0x01); // pointer
580 void reset_nvram(void)
582 partition_t cpulog0, cpulog1;
586 } __attribute__((packed)) header;
588 DEBUG("Erasing NVRAM\n");
589 erase_nvram(0, NVRAM_LENGTH);
591 DEBUG("Creating CPU log partitions\n");
592 header.prefix = be32_to_cpu(LLFW_LOG_BE0_NAME_PREFIX);
593 header.name = be64_to_cpu(LLFW_LOG_BE0_NAME);
594 cpulog0=create_nvram_partition(LLFW_LOG_BE0_SIGNATURE, (char *)&header,
595 (LLFW_LOG_BE0_LENGTH*16)-PARTITION_HEADER_SIZE);
597 header.prefix = be32_to_cpu(LLFW_LOG_BE1_NAME_PREFIX);
598 header.name = be64_to_cpu(LLFW_LOG_BE1_NAME);
599 cpulog1=create_nvram_partition(LLFW_LOG_BE1_SIGNATURE, (char *)&header,
600 (LLFW_LOG_BE1_LENGTH*16)-PARTITION_HEADER_SIZE);
602 DEBUG("Initializing CPU log partitions\n");
603 init_cpulog_partition(cpulog0);
604 init_cpulog_partition(cpulog1);
606 nvramlog_printf("Creating common NVRAM partition\r\n");
607 create_nvram_partition(0x70, "common", 0x01000-PARTITION_HEADER_SIZE);
609 create_free_partition();
612 void nvram_debug(void)
615 printf("\nNVRAM_BASE: %p\n", nvram);
616 printf("NVRAM_LEN: 0x%x\n", NVRAM_LENGTH);
620 unsigned int get_nvram_size(void)