diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/SLOF/lib/libnvram | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/SLOF/lib/libnvram')
-rw-r--r-- | qemu/roms/SLOF/lib/libnvram/Makefile | 53 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libnvram/envvar.c | 243 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libnvram/libnvram.code | 287 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libnvram/libnvram.in | 42 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libnvram/nvram.c | 623 | ||||
-rw-r--r-- | qemu/roms/SLOF/lib/libnvram/nvram.h | 73 |
6 files changed, 1321 insertions, 0 deletions
diff --git a/qemu/roms/SLOF/lib/libnvram/Makefile b/qemu/roms/SLOF/lib/libnvram/Makefile new file mode 100644 index 000000000..d4e9a617e --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/Makefile @@ -0,0 +1,53 @@ +# ***************************************************************************** +# * Copyright (c) 2004, 2008 IBM Corporation +# * All rights reserved. +# * This program and the accompanying materials +# * are made available under the terms of the BSD License +# * which accompanies this distribution, and is available at +# * http://www.opensource.org/licenses/bsd-license.php +# * +# * Contributors: +# * IBM Corporation - initial implementation +# ****************************************************************************/ + +SRCS = nvram.c envvar.c + +TOPCMNDIR ?= ../.. + +include $(TOPCMNDIR)/make.rules + +ASFLAGS = $(FLAG) $(RELEASE) $(CPUARCHDEF) -Wa,-mregnames +CPPFLAGS = -I../libc/include $(CPUARCHDEF) $(FLAG) \ + -I$(INCLBRDDIR) -I$(INCLCMNDIR)/$(CPUARCH) -I. -I../../include +LDFLAGS = -nostdlib + +TARGET = ../libnvram.a + +all: $(TARGET) + +OBJS = $(SRCS:%.c=%.o) + +$(TARGET): $(OBJS) + $(AR) -rc $@ $(OBJS) + $(RANLIB) $@ + + +clean: + $(RM) $(TARGET) $(OBJS) + +distclean: clean + $(RM) Makefile.dep + + +# Rules for creating the dependency file: +depend: + $(RM) Makefile.dep + $(MAKE) Makefile.dep + +Makefile.dep: Makefile + $(CC) -MM $(CPPFLAGS) $(CFLAGS) $(SRCS) > Makefile.dep + + +# Include dependency file if available: +-include Makefile.dep + diff --git a/qemu/roms/SLOF/lib/libnvram/envvar.c b/qemu/roms/SLOF/lib/libnvram/envvar.c new file mode 100644 index 000000000..87aaf27a0 --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/envvar.c @@ -0,0 +1,243 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include <stdint.h> +#include "../libc/include/stdio.h" +#include "../libc/include/string.h" +#include "../libc/include/stdlib.h" +#include "nvram.h" + +/* returns the offset of the first byte after the searched envvar */ +static int get_past_env_pos(partition_t part, char *envvar) +{ + int offset, len; + static char temp[256]; + uint8_t data; + + offset=part.addr; + + memset(temp, 0, 256); + + do { + len=0; + while((data=nvram_read_byte(offset++)) && len < 256) { + temp[len++]=data; + } + if (!strncmp(envvar, temp, strlen(envvar))) { + return offset; + } + } while (len); + + return -1; +} + +/** + * @param partition name of the envvar partition + * @param envvar name of the environment variable + * @return pointer to temporary string containing the value of envvar + */ + +char *get_env(partition_t part, char *envvar) +{ + static char temp[256+1]; + int len, offset; + uint8_t data; + + DEBUG("get_env %s... ", envvar); + if(!part.addr) { + /* ERROR: No environment variable partition */ + DEBUG("invalid partition.\n"); + return NULL; + } + + offset=part.addr; + + do { + len=0; + while((data=nvram_read_byte(offset++)) && len < 256) { + temp[len++]=data; + } + temp[len]=0; + + if (!strncmp(envvar, temp, strlen(envvar))) { + int pos=0; + while (temp[pos]!='=' && pos < len) pos++; + // DEBUG("value='%s'\n", temp+pos+1); + return temp+pos+1; + } + } while (len); + + DEBUG("not found\n"); + return NULL; +} + +static int find_last_envvar(partition_t part) +{ + uint8_t last, current; + int offset; + + offset=part.addr; + + last=nvram_read_byte(part.addr); + + for (offset=part.addr; offset<(int)(part.addr+part.len); offset++) { + current=nvram_read_byte(offset); + if(!last && !current) + return offset; + + last=current; + } + + return -1; +} + +int add_env(partition_t part, char *envvar, char *value) +{ + int freespace, last, len, offset; + unsigned int i; + + /* Find offset where we can write */ + last = find_last_envvar(part); + + /* How much space do we have left? */ + freespace = part.addr+part.len-last; + + /* how long is the entry we want to write? */ + len = strlen(envvar) + strlen(value) + 2; + + if(freespace<len) { + // TODO try to increase partition size + return -1; + } + + offset=last; + + for(i=0; i<strlen(envvar); i++) + nvram_write_byte(offset++, envvar[i]); + + nvram_write_byte(offset++, '='); + + for(i=0; i<strlen(value); i++) + nvram_write_byte(offset++, value[i]); + + return 0; +} + +int del_env(partition_t part, char *envvar) +{ + int last, current, pos, i; + char *buffer; + + if(!part.addr) + return -1; + + last=find_last_envvar(part); + current = pos = get_past_env_pos(part, envvar); + + // TODO is this really required? + /* go back to non-0 value */ + current--; + + while (nvram_read_byte(current)) + current--; + + // TODO is this required? + current++; + + buffer=get_nvram_buffer(last-pos); + + for (i=0; i<last-pos; i++) + buffer[i]=nvram_read_byte(i+pos); + + for (i=0; i<last-pos; i++) + nvram_write_byte(i+current, buffer[i]); + + free_nvram_buffer(buffer); + + erase_nvram(last, current+last-pos); + + return 0; +} + +int set_env(partition_t part, char *envvar, char *value) +{ + char *oldvalue, *buffer; + int last, current, buffersize, i; + + DEBUG("set_env %lx[%lx]: %s=%s\n", part.addr, part.len, envvar, value); + + if(!part.addr) + return -1; + + /* Check whether the environment variable exists already */ + oldvalue = get_env(part, envvar); + + if(oldvalue==NULL) + return add_env(part, envvar, value); + + + /* The value did not change. So we succeeded! */ + if(!strncmp(oldvalue, value, strlen(value)+1)) + return 0; + + /* we need to overwrite environment variables, back them up first */ + + // DEBUG("overwriting existing environment variable\n"); + + /* allocate a buffer */ + last=find_last_envvar(part); + current=get_past_env_pos(part, envvar); + buffersize = last - current; + buffer=get_nvram_buffer(buffersize); + if(!buffer) + return -1; + + for (i=0; i<buffersize; i++) { + buffer[i] = nvram_read_byte(current+i); + } + + /* walk back until the = */ + while (nvram_read_byte(current)!='=') { + current--; + } + + /* Start at envvar= */ + current++; + + /* Write the new value */ + for(i=0; i<(int)strlen(value); i++) { + nvram_write_byte(current++, value[i]); + } + + /* Write end of string marker */ + nvram_write_byte(current++, 0); + + /* Copy back the buffer */ + for (i=0; i<buffersize; i++) { + nvram_write_byte(current++, buffer[i]); + } + + free_nvram_buffer(buffer); + + /* If the new environment variable content is shorter than the old one, + * we need to erase the rest of the bytes + */ + + if (current<last) { + for(i=current; i<last; i++) { + nvram_write_byte(i, 0); + } + } + + return 0; /* success */ +} + diff --git a/qemu/roms/SLOF/lib/libnvram/libnvram.code b/qemu/roms/SLOF/lib/libnvram/libnvram.code new file mode 100644 index 000000000..723941d3e --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/libnvram.code @@ -0,0 +1,287 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ +#include <nvram.h> + +#define STRING_INIT(str) \ + char str[255]; \ + char * str##_address; \ + int str##_length; + +#define STRING_FROM_STACK(str) \ + str##_length = TOS.u; POP; \ + str##_address = TOS.a; POP; \ + memcpy(str, str##_address, str##_length); \ + memset(str + str##_length, 0, 255 - str##_length); + +PRIM(nvram_X2d_c_X40) + unsigned int offset = TOS.u; + TOS.u=nvram_read_byte(offset); +MIRP + +PRIM(nvram_X2d_w_X40) + unsigned int offset = TOS.u; + TOS.u=nvram_read_word(offset); +MIRP + +PRIM(nvram_X2d_l_X40) + unsigned int offset = TOS.u; + TOS.u=nvram_read_dword(offset); +MIRP + +PRIM(nvram_X2d_x_X40) + unsigned int offset = TOS.u; + TOS.u=nvram_read_qword(offset); +MIRP + +PRIM(nvram_X2d_c_X21) + nvram_write_byte(TOS.u, NOS.u); + POP; POP; +MIRP + +PRIM(nvram_X2d_w_X21) + nvram_write_word(TOS.u, NOS.u); + POP; POP; +MIRP + +PRIM(nvram_X2d_l_X21) + nvram_write_dword(TOS.u, NOS.u); + POP; POP; +MIRP + +PRIM(nvram_X2d_x_X21) + nvram_write_qword(TOS.u, NOS.u); + POP; POP; +MIRP + +/* get-nvram-partition ( type -- addr len FAILED? ) */ +PRIM(get_X2d_nvram_X2d_partition) + partition_t partition; + unsigned int ptype = TOS.u; + partition = get_partition(ptype, NULL); + if(partition.len && partition.len != -1) { + TOS.u = partition.addr; + PUSH; + TOS.u = partition.len; + PUSH; + TOS.u = 0; // FALSE + } else { + TOS.u = -1; // TRUE + } +MIRP + +/* get-named-nvram-partition ( name.addr name.len -- addr len FAILED? ) */ +PRIM(get_X2d_named_X2d_nvram_X2d_partition) + STRING_INIT(name) + partition_t partition; + + STRING_FROM_STACK(name) + partition = get_partition(-1, name); + + if(partition.len && partition.len != -1) { + PUSH; + TOS.u = partition.addr; + PUSH; + TOS.u = partition.len; + PUSH; + TOS.u = 0; // FALSE + } else { + PUSH; + TOS.u = -1; // TRUE + } +MIRP + + + +/* new-nvram-partition ( type name.addr name.len len -- part.offs part.len FALSE | TRUE) */ +PRIM(new_X2d_nvram_X2d_partition) + int type, len, i, slen; + char name[12], *addr; + partition_t partition; + + len = TOS.u; POP; + slen = TOS.u; POP; + addr = (char *)TOS.u; POP; + type = TOS.u; POP; + + for (i=0; i<12; i++) { + if(slen>i) + name[i]=addr[i]; + else + name[i]=0; + } + + partition=new_nvram_partition(type, name, len); + + if(!partition.len) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = partition.addr; + PUSH; TOS.u = partition.len; + PUSH; TOS.u = 0; // FALSE + } +MIRP + +/* inrease-nvram-partition ( part.offs part.len new-len -- FALSE | TRUE ) */ +PRIM(increase_X2d_nvram_X2d_partition) + int len, ret; + partition_t partition; + + // FIXME + partition.addr = TOS.u; POP; + partition.len = TOS.u; POP; + len = TOS.u; POP; + + ret=increase_nvram_partition_size(partition, len); + + PUSH; + + if(!ret) + TOS.u=-1; // TRUE + else + TOS.u=0; // FALSE + +MIRP + +PRIM(internal_X2d_reset_X2d_nvram) + reset_nvram(); +MIRP + +PRIM(wipe_X2d_nvram) + wipe_nvram(); +MIRP + +PRIM(nvram_X2d_debug) + nvram_debug(); +MIRP + +// ( part.start part.len name.addr name.len -- var.addr var.len TRUE | false ) +PRIM(internal_X2d_get_X2d_env) + STRING_INIT(name) + partition_t part; + char *val; + + STRING_FROM_STACK(name) + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + val=get_env(part, name); + if(val) { + PUSH; TOS.a = val; + PUSH; TOS.u = strlen(val); + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } +MIRP + +// ( part.start part.len name.addr name.len val.addr val.len -- FALSE|TRUE) +PRIM(internal_X2d_add_X2d_env) + STRING_INIT(name) + STRING_INIT(value) + partition_t part; + int ret; + + STRING_FROM_STACK(value) + STRING_FROM_STACK(name) + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + ret=add_env(part, name, value); + if(ret) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } +MIRP + +// ( part.addr part.len name.addr name.len -- FALSE|TRUE) +PRIM(internal_X2d_del_X2d_env) + STRING_INIT(name) + partition_t part; + int ret; + + STRING_FROM_STACK(name); + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + ret=del_env(part, name); + if(ret) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } + +MIRP + +// internal-set-env ( part.addr part.len name.addr name.len val.addr val.len -- FALSE|TRUE) +PRIM(internal_X2d_set_X2d_env) + STRING_INIT(name) + STRING_INIT(value) + partition_t part; + int ret; + + STRING_FROM_STACK(value) + STRING_FROM_STACK(name) + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + ret=set_env(part, name, value); + if(ret) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } +MIRP + +// ( part.addr part.len -- FALSE|TRUE) +PRIM(erase_X2d_nvram_X2d_partition) + partition_t part; + int ret; + + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + ret=clear_nvram_partition(part); + if(ret) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } + +MIRP + +// ( part.addr part.len -- FALSE|TRUE) +PRIM(delete_X2d_nvram_X2d_partition) + partition_t part; + int ret; + + part.len = TOS.u; POP; + part.addr = TOS.u; POP; + + ret=delete_nvram_partition(part); + if(ret) { + PUSH; TOS.u = -1; // TRUE + } else { + PUSH; TOS.u = 0; // FALSE + } + +MIRP + +// ( fetch_token store_token size nvram-addr -- ) +PRIM(internal_X2d_nvram_X2d_init) + void *nvram_addr = TOS.a; POP; + uint32_t nvram_size = TOS.u; POP; + uint32_t store_token = TOS.u; POP; + long fetch_token = TOS.u; POP; + + nvram_init(fetch_token, store_token, nvram_size, nvram_addr); +MIRP diff --git a/qemu/roms/SLOF/lib/libnvram/libnvram.in b/qemu/roms/SLOF/lib/libnvram/libnvram.in new file mode 100644 index 000000000..bbb20a889 --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/libnvram.in @@ -0,0 +1,42 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +/* NVRAM access primitives */ +cod(nvram-c@) +cod(nvram-c!) +cod(nvram-w@) +cod(nvram-w!) +cod(nvram-l@) +cod(nvram-l!) +cod(nvram-x@) +cod(nvram-x!) + +/* Generic NVRAM helpers */ +cod(internal-reset-nvram) +cod(nvram-debug) +cod(wipe-nvram) + +/* NVRAM Partition Handling */ +cod(get-nvram-partition) +cod(get-named-nvram-partition) +cod(new-nvram-partition) +cod(increase-nvram-partition) +cod(erase-nvram-partition) +cod(delete-nvram-partition) + +/* NVRAM environment access words */ +cod(internal-get-env) +cod(internal-add-env) +cod(internal-del-env) +cod(internal-set-env) + +cod(internal-nvram-init) diff --git a/qemu/roms/SLOF/lib/libnvram/nvram.c b/qemu/roms/SLOF/lib/libnvram/nvram.c new file mode 100644 index 000000000..5c1137669 --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/nvram.c @@ -0,0 +1,623 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#include "cache.h" +#include "nvram.h" +#include "../libhvcall/libhvcall.h" + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <southbridge.h> +#include <nvramlog.h> +#include <byteorder.h> + +#ifdef RTAS_NVRAM +static uint32_t fetch_token; +static uint32_t store_token; +static uint32_t NVRAM_LENGTH; +static char *nvram_buffer; /* use buffer allocated by SLOF code */ +#else +#ifndef NVRAM_LENGTH +#define NVRAM_LENGTH 0x10000 +#endif +/* + * This is extremely ugly, but still better than implementing + * another sbrk() around it. + */ +static char nvram_buffer[NVRAM_LENGTH]; +#endif + +static uint8_t nvram_buffer_locked=0x00; + +void nvram_init(uint32_t _fetch_token, uint32_t _store_token, + long _nvram_length, void* nvram_addr) +{ +#ifdef RTAS_NVRAM + fetch_token = _fetch_token; + store_token = _store_token; + NVRAM_LENGTH = _nvram_length; + nvram_buffer = nvram_addr; + + DEBUG("\nNVRAM: size=%d, fetch=%x, store=%x\n", + NVRAM_LENGTH, fetch_token, store_token); +#endif +} + + +void asm_cout(long Character,long UART,long NVRAM); + +#if defined(DISABLE_NVRAM) + +static volatile uint8_t nvram[NVRAM_LENGTH]; /* FAKE */ + +#define nvram_access(type,size,name) \ + type nvram_read_##name(unsigned int offset) \ + { \ + type *pos; \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return 0; \ + pos = (type *)(nvram+offset); \ + return *pos; \ + } \ + void nvram_write_##name(unsigned int offset, type data) \ + { \ + type *pos; \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return; \ + pos = (type *)(nvram+offset); \ + *pos = data; \ + } + +#elif defined(RTAS_NVRAM) + +static inline void nvram_fetch(unsigned int offset, void *buf, unsigned int len) +{ + struct hv_rtas_call rtas = { + .token = fetch_token, + .nargs = 3, + .nrets = 2, + .argret = { offset, (uint32_t)(unsigned long)buf, len }, + }; + h_rtas(&rtas); +} + +static inline void nvram_store(unsigned int offset, void *buf, unsigned int len) +{ + struct hv_rtas_call rtas = { + .token = store_token, + .nargs = 3, + .nrets = 2, + .argret = { offset, (uint32_t)(unsigned long)buf, len }, + }; + h_rtas(&rtas); +} + +#define nvram_access(type,size,name) \ + type nvram_read_##name(unsigned int offset) \ + { \ + type val; \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return 0; \ + nvram_fetch(offset, &val, size / 8); \ + return val; \ + } \ + void nvram_write_##name(unsigned int offset, type data) \ + { \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return; \ + nvram_store(offset, &data, size / 8); \ + } + +#else /* DISABLE_NVRAM */ + +static volatile uint8_t *nvram = (volatile uint8_t *)SB_NVRAM_adr; + +#define nvram_access(type,size,name) \ + type nvram_read_##name(unsigned int offset) \ + { \ + type *pos; \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return 0; \ + pos = (type *)(nvram+offset); \ + return ci_read_##size(pos); \ + } \ + void nvram_write_##name(unsigned int offset, type data) \ + { \ + type *pos; \ + if (offset > (NVRAM_LENGTH - sizeof(type))) \ + return; \ + pos = (type *)(nvram+offset); \ + ci_write_##size(pos, data); \ + } + +#endif + +/* + * producer for nvram access functions. Since these functions are + * basically all the same except for the used data types, produce + * them via the nvram_access macro to keep the code from bloating. + */ + +nvram_access(uint8_t, 8, byte) +nvram_access(uint16_t, 16, word) +nvram_access(uint32_t, 32, dword) +nvram_access(uint64_t, 64, qword) + + + +/** + * This function is a minimal abstraction for our temporary + * buffer. It should have been malloced, but since there is no + * usable malloc, we go this route. + * + * @return pointer to temporary buffer + */ + +char *get_nvram_buffer(int len) +{ + if(len>NVRAM_LENGTH) + return NULL; + + if(nvram_buffer_locked) + return NULL; + + nvram_buffer_locked = 0xff; + + return nvram_buffer; +} + +/** + * @param buffer pointer to the allocated buffer. This + * is unused, but nice in case we ever get a real malloc + */ + +void free_nvram_buffer(char *buffer __attribute__((unused))) +{ + nvram_buffer_locked = 0x00; +} + +/** + * @param fmt format string, like in printf + * @param ... variable number of arguments + */ + +int nvramlog_printf(const char* fmt, ...) +{ + char buff[256]; + int count, i; + va_list ap; + + va_start(ap, fmt); + count = vsprintf(buff, fmt, ap); + va_end(ap); + + for (i=0; i<count; i++) + asm_cout(buff[i], 0, 1); + + return count; +} + +/** + * @param offset start offset of the partition header + */ + +static uint8_t get_partition_type(int offset) +{ + return nvram_read_byte(offset); +} + +/** + * @param offset start offset of the partition header + */ + +static uint8_t get_partition_header_checksum(int offset) +{ + return nvram_read_byte(offset+1); +} + +/** + * @param offset start offset of the partition header + */ + +static uint16_t get_partition_len(int offset) +{ + return nvram_read_word(offset+2); +} + +/** + * @param offset start offset of the partition header + * @return static char array containing the partition name + * + * NOTE: If the partition name needs to be non-temporary, strdup + * and use the copy instead. + */ + +static char * get_partition_name(int offset) +{ + static char name[12]; + int i; + for (i=0; i<12; i++) + name[i]=nvram_read_byte(offset+4+i); + + DEBUG("name: \"%s\"\n", name); + return name; +} + +static uint8_t calc_partition_header_checksum(int offset) +{ + uint16_t plainsum; + uint8_t checksum; + int i; + + plainsum = nvram_read_byte(offset); + + for (i=2; i<PARTITION_HEADER_SIZE; i++) + plainsum+=nvram_read_byte(offset+i); + + checksum=(plainsum>>8)+(plainsum&0xff); + + return checksum; +} + +static int calc_used_nvram_space(void) +{ + int walk, len; + + for (walk=0; walk<NVRAM_LENGTH;) { + if(nvram_read_byte(walk) == 0 + || get_partition_header_checksum(walk) != + calc_partition_header_checksum(walk)) { + /* If there's no valid entry, bail out */ + break; + } + + len=get_partition_len(walk); + DEBUG("... part len=%x, %x\n", len, len*16); + + if(!len) { + /* If there's a partition type but no len, bail out. + * Don't bail out if type is 0. This can be used to + * find the offset of the first free byte. + */ + break; + } + + walk += len * 16; + } + DEBUG("used nvram space: %d\n", walk); + + return walk; +} + +/** + * + * @param type partition type. Set this to the partition type you are looking + * for. If there are several partitions with the same type, only + * the first partition with that type will be found. + * Set to -1 to ignore. Set to 0 to find free unpartitioned space. + * + * @param name partition name. Set this to the name of the partition you are + * looking for. If there are several partitions with the same name, + * only the first partition with that name will be found. + * Set to NULL to ignore. + * + * To disambiguate the partitions you should have a unique name if you plan to + * have several partitions of the same type. + * + */ + +partition_t get_partition(unsigned int type, char *name) +{ + partition_t ret={0,-1}; + int walk, len; + + DEBUG("get_partition(%i, '%s')\n", type, name); + + for (walk=0; walk<NVRAM_LENGTH;) { + // DEBUG("get_partition: walk=%x\n", walk); + if(get_partition_header_checksum(walk) != + calc_partition_header_checksum(walk)) { + /* If there's no valid entry, bail out */ + break; + } + + len=get_partition_len(walk); + if(type && !len) { + /* If there's a partition type but no len, bail out. + * Don't bail out if type is 0. This can be used to + * find the offset of the first free byte. + */ + break; + } + + /* Check if either type or name or both do not match. */ + if ( (type!=(unsigned int)-1 && type != get_partition_type(walk)) || + (name && strncmp(get_partition_name(walk), name, 12)) ) { + /* We hit another partition. Continue + * at the end of this partition + */ + walk += len*16; + continue; + } + + ret.addr=walk+PARTITION_HEADER_SIZE; + ret.len=(len*16)-PARTITION_HEADER_SIZE; + break; + } + + return ret; +} + +void erase_nvram(int offset, int len) +{ + int i; + + for (i=offset; i<offset+len; i++) + nvram_write_byte(i, 0); +} + +void wipe_nvram(void) +{ + erase_nvram(0, NVRAM_LENGTH); +} + +/** + * @param partition partition structure pointing to the partition to wipe. + * @param header_only if header_only is != 0 only the partition header is + * nulled out, not the whole partition. + */ + +int wipe_partition(partition_t partition, int header_only) +{ + int pstart, len; + + pstart=partition.addr-PARTITION_HEADER_SIZE; + + len=PARTITION_HEADER_SIZE; + + if(!header_only) + len += partition.len; + + erase_nvram(pstart, len); + + return 0; +} + + +static partition_t create_nvram_partition(int type, const char *name, int len) +{ + partition_t ret = { 0, 0 }; + int offset, plen; + unsigned int i; + + plen = ALIGN(len+PARTITION_HEADER_SIZE, 16); + + DEBUG("Creating partition type=%x, name=%s, len=%d plen=%d\n", + type, name, len, plen); + + offset = calc_used_nvram_space(); + + if (NVRAM_LENGTH-(calc_used_nvram_space())<plen) { + DEBUG("Not enough free space.\n"); + return ret; + } + + DEBUG("Writing header."); + + nvram_write_byte(offset, type); + nvram_write_word(offset+2, plen/16); + + for (i=0; i<strlen(name); i++) + nvram_write_byte(offset+4+i, name[i]); + + nvram_write_byte(offset+1, calc_partition_header_checksum(offset)); + + ret.addr = offset+PARTITION_HEADER_SIZE; + ret.len = len; + + DEBUG("partition created: addr=%lx len=%lx\n", ret.addr, ret.len); + + return ret; +} + +static int create_free_partition(void) +{ + int free_space; + partition_t free_part; + + free_space = NVRAM_LENGTH - calc_used_nvram_space() - PARTITION_HEADER_SIZE; + free_part = create_nvram_partition(0x7f, "free space", free_space); + + return (free_part.addr != 0); +} + +partition_t new_nvram_partition(int type, char *name, int len) +{ + partition_t free_part, new_part = { 0, 0 }; + + /* NOTE: Assume all free space is consumed by the "free space" + * partition. This means a partition can not be increased in the middle + * of reset_nvram, which is obviously not a big loss. + */ + + free_part=get_partition(0x7f, NULL); + if( free_part.len && free_part.len != -1) + wipe_partition(free_part, 1); + + new_part = create_nvram_partition(type, name, len); + + if(new_part.len != len) { + new_part.len = 0; + new_part.addr = 0; + } + + create_free_partition(); + + return new_part; +} + +/** + * @param partition partition structure pointing to the partition to wipe. + */ + +int delete_nvram_partition(partition_t partition) +{ + int i; + partition_t free_part; + + if(!partition.len || partition.len == -1) + return 0; + + for (i=partition.addr+partition.len; i< NVRAM_LENGTH; i++) + nvram_write_byte(i - partition.len - PARTITION_HEADER_SIZE, nvram_read_byte(i)); + + erase_nvram(NVRAM_LENGTH-partition.len-PARTITION_HEADER_SIZE, + partition.len-PARTITION_HEADER_SIZE); + + free_part=get_partition(0x7f, NULL); + wipe_partition(free_part, 0); + create_free_partition(); + + return 1; +} + +int clear_nvram_partition(partition_t part) +{ + if(!part.addr) + return 0; + + erase_nvram(part.addr, part.len); + + return 1; +} + + +int increase_nvram_partition_size(partition_t partition, int newsize) +{ + partition_t free_part; + int free_offset, end_offset, i; + + /* We don't support shrinking partitions (yet) */ + if (newsize < partition.len) { + return 0; + } + + /* NOTE: Assume all free space is consumed by the "free space" + * partition. This means a partition can not be increased in the middle + * of reset_nvram, which is obviously not a big loss. + */ + + free_part=get_partition(0x7f, NULL); + + // FIXME: It could be 16 byte more. Also handle empty "free" partition. + if (free_part.len == -1 || free_part.len < newsize - partition.len ) { + return 0; + } + + free_offset=free_part.addr - PARTITION_HEADER_SIZE; // first unused byte + end_offset=partition.addr + partition.len; // last used byte of partition + 1 + + if(free_offset > end_offset) { + int j, bufferlen; + char *overlap_buffer; + + bufferlen=free_offset - end_offset; + + overlap_buffer=get_nvram_buffer(bufferlen); + if(!overlap_buffer) { + return 0; + } + + for (i=end_offset, j=0; i<free_offset; i++, j++) + overlap_buffer[j]=nvram_read_byte(i); + + /* Only wipe the header. The free space partition is empty per + * definition + */ + + wipe_partition(free_part, 1); + + for (i=partition.addr+newsize, j=0; i<(int)(partition.addr+newsize+bufferlen); i++, j++) + nvram_write_byte(i, overlap_buffer[j]); + + free_nvram_buffer(overlap_buffer); + } else { + /* Only wipe the header. */ + wipe_partition(free_part, 1); + } + + /* Clear the new partition space */ + erase_nvram(partition.addr+partition.len, newsize-partition.len); + + nvram_write_word(partition.addr - 16 + 2, newsize); + + create_free_partition(); + + return 1; +} + +static void init_cpulog_partition(partition_t cpulog) +{ + unsigned int offset=cpulog.addr; + + /* see board-xxx/include/nvramlog.h for information */ + nvram_write_word(offset+0, 0x40); // offset + nvram_write_word(offset+2, 0x00); // flags + nvram_write_dword(offset+4, 0x01); // pointer + +} + +void reset_nvram(void) +{ + partition_t cpulog0, cpulog1; + struct { + uint32_t prefix; + uint64_t name; + } __attribute__((packed)) header; + + DEBUG("Erasing NVRAM\n"); + erase_nvram(0, NVRAM_LENGTH); + + DEBUG("Creating CPU log partitions\n"); + header.prefix = be32_to_cpu(LLFW_LOG_BE0_NAME_PREFIX); + header.name = be64_to_cpu(LLFW_LOG_BE0_NAME); + cpulog0=create_nvram_partition(LLFW_LOG_BE0_SIGNATURE, (char *)&header, + (LLFW_LOG_BE0_LENGTH*16)-PARTITION_HEADER_SIZE); + + header.prefix = be32_to_cpu(LLFW_LOG_BE1_NAME_PREFIX); + header.name = be64_to_cpu(LLFW_LOG_BE1_NAME); + cpulog1=create_nvram_partition(LLFW_LOG_BE1_SIGNATURE, (char *)&header, + (LLFW_LOG_BE1_LENGTH*16)-PARTITION_HEADER_SIZE); + + DEBUG("Initializing CPU log partitions\n"); + init_cpulog_partition(cpulog0); + init_cpulog_partition(cpulog1); + + nvramlog_printf("Creating common NVRAM partition\r\n"); + create_nvram_partition(0x70, "common", 0x01000-PARTITION_HEADER_SIZE); + + create_free_partition(); +} + +void nvram_debug(void) +{ +#ifndef RTAS_NVRAM + printf("\nNVRAM_BASE: %p\n", nvram); + printf("NVRAM_LEN: 0x%x\n", NVRAM_LENGTH); +#endif +} + +unsigned int get_nvram_size(void) +{ + return NVRAM_LENGTH; +} diff --git a/qemu/roms/SLOF/lib/libnvram/nvram.h b/qemu/roms/SLOF/lib/libnvram/nvram.h new file mode 100644 index 000000000..fa6bdd425 --- /dev/null +++ b/qemu/roms/SLOF/lib/libnvram/nvram.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * Copyright (c) 2004, 2008 IBM Corporation + * All rights reserved. + * This program and the accompanying materials + * are made available under the terms of the BSD License + * which accompanies this distribution, and is available at + * http://www.opensource.org/licenses/bsd-license.php + * + * Contributors: + * IBM Corporation - initial implementation + *****************************************************************************/ + +#ifndef __NVRAM_H +#define __NVRAM_H 1 + +/* data structures */ + +typedef struct { + unsigned long addr; + long len; +} partition_t; + +/* macros */ + +#define DEBUG(x...) +// #define DEBUG(x...) printf(x); + +#ifndef ALIGN +#define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) +#endif + +#define NULL ((void *)0) + +#define PARTITION_HEADER_SIZE 16 + + +/* exported functions */ + +#define nvram_access_proto(type,name) \ + type nvram_read_##name(unsigned int offset); \ + void nvram_write_##name(unsigned int offset, type data); + +nvram_access_proto(uint8_t, byte) +nvram_access_proto(uint16_t, word) +nvram_access_proto(uint32_t, dword) +nvram_access_proto(uint64_t, qword) + +/* nvram.c */ + +char *get_nvram_buffer(int len); +void free_nvram_buffer(char *buffer); +int nvramlog_printf(const char* fmt, ...); +partition_t get_partition(unsigned int type, char *name); +void erase_nvram(int offset, int len); +int wipe_partition(partition_t partition, int header_only); +partition_t new_nvram_partition(int type, char *name, int len); +int increase_nvram_partition_size(partition_t partition, int newsize); +int clear_nvram_partition(partition_t part); +int delete_nvram_partition(partition_t part); +void reset_nvram(void); +void wipe_nvram(void); +void nvram_debug(void); +void nvram_init(uint32_t store_token, uint32_t fetch_token, + long nv_size, void* nvram_addr); +unsigned int get_nvram_size(void); + +/* envvar.c */ +char *get_env(partition_t part, char *envvar); +int add_env(partition_t part, char *envvar, char *value); +int del_env(partition_t part, char *envvar); +int set_env(partition_t part, char *envvar, char *value); + +#endif |