blob: 2a04622b1c85c43eadfaa19ec20145697ca1308e [file] [log] [blame]
/*
* Copyright 2013, Google Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*/
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <zlib.h>
#include "flash.h"
#include "fdtmap.h"
#include "flash.h"
#include "libfdt.h"
#include "layout.h"
#ifdef DEBUG
#define debug(a, b...) printf(a, ##b)
#else
#define debug(a, b...)
#endif
/* An entry in the flashmap - we only care about the offset and length */
struct fmap_entry {
uint32_t offset;
uint32_t length;
};
/**
* Look up a property in a node and check that it has a minimum length.
*
* @blob: FDT blob
* @node: node to examine
* @prop_name: name of property to find
* @min_len: minimum property length in bytes
* @err: 0 if ok, or -FDT_ERR_NOTFOUND if the property is not
found, or -FDT_ERR_BADLAYOUT if not enough data
* @return pointer to cell, which is only valid if err == 0
*/
static const void *get_prop_check_min_len(const void *blob, int node,
const char *prop_name, int min_len, int *err)
{
const void *cell;
int len;
debug("%s: %s\n", __func__, prop_name);
cell = fdt_getprop(blob, node, prop_name, &len);
if (!cell)
*err = -FDT_ERR_NOTFOUND;
else if (len < min_len)
*err = -FDT_ERR_BADLAYOUT;
else
*err = 0;
return cell;
}
static int fdtdec_get_int_array(const void *blob, int node,
const char *prop_name, uint32_t *array, int count)
{
const uint32_t *cell;
int i, err = 0;
debug("%s: %s\n", __func__, prop_name);
cell = get_prop_check_min_len(blob, node, prop_name,
sizeof(uint32_t) * count, &err);
if (!err) {
for (i = 0; i < count; i++)
array[i] = fdt32_to_cpu(cell[i]);
}
return err;
}
/**
* Read a flash entry from the fdt
*
* @blob: FDT blob
* @node: Offset of node to read
* @name: Name of node being read
* @entry: Place to put offset and size of this node
* @return 0 if ok, -ve on error
*/
static int read_entry(const void *blob, int node, const char *name,
struct fmap_entry *entry)
{
uint32_t reg[2];
int ret;
ret = fdtdec_get_int_array(blob, node, "reg", reg, 2);
if (ret) {
debug("Node '%s' has bad/missing 'reg' property\n", name);
return ret;
}
entry->offset = reg[0];
entry->length = reg[1];
return 0;
}
static int scan_flashmap(const void *blob, romlayout_t *rom_entries,
int max_entries)
{
int romimages;
int offset;
int depth;
int node;
offset = fdt_node_offset_by_compatible(blob, -1,
"chromeos,flashmap");
if (offset < 0)
return offset;
for (depth = romimages = 0; offset > 0 && depth >= 0; offset = node) {
struct fmap_entry entry;
const char *name;
romlayout_t *rl;
char *s;
int ret;
node = fdt_next_node(blob, offset, &depth);
if (node < 0)
return node;
if (depth != 1)
continue;
name = fdt_getprop(blob, node, "label", NULL);
ret = read_entry(blob, node, name, &entry);
if (ret)
return ret;
if (romimages >= max_entries) {
msg_gerr("ROM image contains too many regions\n");
return -1;
}
rl = &rom_entries[romimages];
rl->start = entry.offset;
/*
* Flashrom rom entries use absolute addresses. So for non-zero
* length entries, we need to subtract 1 from offset + size to
* determine the end address.
*/
rl->end = entry.offset + entry.length;
if (entry.length)
rl->end--;
/* Use an upper case name for flashrom, with _ instead of - */
strncpy(rl->name, name, sizeof(rl->name));
rl->name[sizeof(rl->name) - 1] = '\0';
for (s = rl->name; *s; s++)
if (*s == '-')
*s = '_';
else if (*s == '@')
break;
else
*s = toupper(*s);
*s = '\0';
*rl->file = '\0';
rl->included = 0;
msg_gdbg("added fdtmap region \"%s\" (file=\"%s\") as %sincluded,"
" offset: 0x%08x, end: 0x%08x\n",
rl->name,
rl->file,
rl->included ? "" : "not ",
rl->start,
rl->end);
romimages++;
}
msg_gdbg("Found %d regions\n", romimages);
return romimages;
}
int fdtmap_add_entries_from_buf(const void *blob, romlayout_t *rom_entries,
int max_entries)
{
int count;
count = scan_flashmap(blob, rom_entries, max_entries);
if (count < 0) {
msg_gerr("Failed to read flashmap: '%s'\n", fdt_strerror(count));
return -1;
}
return count;
}
int fdtmap_find(void *source_handle,
int (*read_chunk)(void *handle,
void *dest,
size_t offset,
size_t size),
struct fdtmap_hdr *hdr,
loff_t offset, uint8_t **buf)
{
int fmap_size;
uint32_t crc;
if (memcmp(hdr->sig, FDTMAP_SIGNATURE, sizeof(hdr->sig)))
return 0;
msg_gdbg("%s: found possible fdtmap at offset %#lx\n",
__func__, (unsigned long)offset);
fmap_size = hdr->size;
*buf = malloc(fmap_size);
msg_gdbg("%s: fdtmap size %#x\n", __func__, fmap_size);
if (read_chunk(source_handle, *buf, offset + sizeof(*hdr), fmap_size)) {
msg_gdbg("[L%d] failed to read %d bytes at offset %#lx\n",
__LINE__, fmap_size, (unsigned long)offset);
return 0;
}
/* Sanity check, the FDT total size should equal fmap_size */
if (fdt_totalsize(*buf) != fmap_size) {
msg_gdbg("[L%d] FDT size %#x did not match header size %#x at %#lx\n",
__LINE__, fdt_totalsize(*buf),
fmap_size, (unsigned long)offset);
return 0;
}
crc = crc32(0, Z_NULL, 0);
crc = crc32(crc, *buf, fmap_size);
/* Sanity check, the FDT total size should equal fmap_size */
if (crc != hdr->crc32) {
msg_gdbg("[L%d] CRC32 %#08x did not match expected %#08x at %#lx\n",
__LINE__, crc, hdr->crc32, (unsigned long)offset);
return 0;
}
return 1;
}