diff options
author | Sol Boucher <solb@chromium.org> | 2015-02-27 17:07:30 -0800 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2015-05-08 19:45:27 +0200 |
commit | 65d9520e15d6ade6b80778536937af5cdb8dfb6d (patch) | |
tree | abce81c24f1ddbb1750f7e8d6bc06df1791602f0 /util/cbfstool/flashmap | |
parent | 636cc858bb6979cbd51abaa1d75842146b0c29f7 (diff) |
cbfstool: Import minimal set of files from flashmap
flashmap was developed in a separate repository until now.
Import the files from the 2012 version of the project [1].
[1] https://code.google.com/p/flashmap
BUG=chromium:461875
TEST=None
BRANCH=None
Change-Id: Ida33f81509abc1cf2e532435adbbf31919d96bd8
Signed-off-by: Sol Boucher <solb@chromium.org>
Original-Commit-Id: f44e1d1864babe244f07ca49655f0b80b84e890d
Original-Change-Id: Ibf191d34df738449c9b9d7ebccca3d7f4150d4d3
Original-Signed-off-by: Sol Boucher <solb@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/254801
Original-Reviewed-by: Julius Werner <jwerner@chromium.org>
Reviewed-on: http://review.coreboot.org/9940
Tested-by: build bot (Jenkins)
Diffstat (limited to 'util/cbfstool/flashmap')
-rw-r--r-- | util/cbfstool/flashmap/fmap.c | 727 | ||||
-rw-r--r-- | util/cbfstool/flashmap/fmap.h | 192 | ||||
-rw-r--r-- | util/cbfstool/flashmap/kv_pair.c | 242 | ||||
-rw-r--r-- | util/cbfstool/flashmap/kv_pair.h | 153 | ||||
-rw-r--r-- | util/cbfstool/flashmap/valstr.c | 65 | ||||
-rw-r--r-- | util/cbfstool/flashmap/valstr.h | 78 |
6 files changed, 1457 insertions, 0 deletions
diff --git a/util/cbfstool/flashmap/fmap.c b/util/cbfstool/flashmap/fmap.c new file mode 100644 index 0000000000..8850a925f1 --- /dev/null +++ b/util/cbfstool/flashmap/fmap.c @@ -0,0 +1,727 @@ +/* Copyright 2010, 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <assert.h> + +#include <fmap.h> +#include <valstr.h> + +#include "kv_pair.h" +#include "mincrypt/sha.h" + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +const struct valstr flag_lut[] = { + { FMAP_AREA_STATIC, "static" }, + { FMAP_AREA_COMPRESSED, "compressed" }, + { FMAP_AREA_RO, "ro" }, +}; + +/* returns size of fmap data structure if successful, <0 to indicate error */ +int fmap_size(struct fmap *fmap) +{ + if (!fmap) + return -1; + + return sizeof(*fmap) + (fmap->nareas * sizeof(struct fmap_area)); +} + +/* brute force linear search */ +static long int fmap_lsearch(const uint8_t *image, size_t len) +{ + long int offset; + int fmap_found = 0; + + for (offset = 0; offset < len - strlen(FMAP_SIGNATURE); offset++) { + if (!memcmp(&image[offset], + FMAP_SIGNATURE, + strlen(FMAP_SIGNATURE))) { + fmap_found = 1; + break; + } + } + + if (!fmap_found) + return -1; + + if (offset + fmap_size((struct fmap *)&image[offset]) > len) + return -1; + + return offset; +} + +/* if image length is a power of 2, use binary search */ +static long int fmap_bsearch(const uint8_t *image, size_t len) +{ + long int offset = -1; + int fmap_found = 0, stride; + + /* + * For efficient operation, we start with the largest stride possible + * and then decrease the stride on each iteration. Also, check for a + * remainder when modding the offset with the previous stride. This + * makes it so that each offset is only checked once. + */ + for (stride = len / 2; stride >= 1; stride /= 2) { + if (fmap_found) + break; + + for (offset = 0; + offset < len - strlen(FMAP_SIGNATURE); + offset += stride) { + if ((offset % (stride * 2) == 0) && (offset != 0)) + continue; + if (!memcmp(&image[offset], + FMAP_SIGNATURE, + strlen(FMAP_SIGNATURE))) { + fmap_found = 1; + break; + } + } + } + + if (!fmap_found) + return -1; + + if (offset + fmap_size((struct fmap *)&image[offset]) > len) + return -1; + + return offset; +} + +static int popcnt(unsigned int u) +{ + int count; + + /* K&R method */ + for (count = 0; u; count++) + u &= (u - 1); + + return count; +} + +long int fmap_find(const uint8_t *image, unsigned int image_len) +{ + long int ret = -1; + + if ((image == NULL) || (image_len == 0)) + return -1; + + if (popcnt(image_len) == 1) + ret = fmap_bsearch(image, image_len); + else + ret = fmap_lsearch(image, image_len); + + return ret; +} + +int fmap_print(const struct fmap *fmap) +{ + int i; + struct kv_pair *kv = NULL; + const uint8_t *tmp; + + kv = kv_pair_new(); + if (!kv) + return -1; + + tmp = fmap->signature; + kv_pair_fmt(kv, "fmap_signature", + "0x%02x%02x%02x%02x%02x%02x%02x%02x", + tmp[0], tmp[1], tmp[2], tmp[3], + tmp[4], tmp[5], tmp[6], tmp[7]); + kv_pair_fmt(kv, "fmap_ver_major", "%d", fmap->ver_major); + kv_pair_fmt(kv, "fmap_ver_minor","%d", fmap->ver_minor); + kv_pair_fmt(kv, "fmap_base", "0x%016llx", + (unsigned long long)fmap->base); + kv_pair_fmt(kv, "fmap_size", "0x%04x", fmap->size); + kv_pair_fmt(kv, "fmap_name", "%s", fmap->name); + kv_pair_fmt(kv, "fmap_nareas", "%d", fmap->nareas); + kv_pair_print(kv); + kv_pair_free(kv); + + for (i = 0; i < fmap->nareas; i++) { + struct kv_pair *kv; + uint16_t flags; + char *str; + + kv = kv_pair_new(); + if (!kv) + return -1; + + kv_pair_fmt(kv, "area_offset", "0x%08x", + fmap->areas[i].offset); + kv_pair_fmt(kv, "area_size", "0x%08x", + fmap->areas[i].size); + kv_pair_fmt(kv, "area_name", "%s", + fmap->areas[i].name); + kv_pair_fmt(kv, "area_flags_raw", "0x%02x", + fmap->areas[i].flags); + + /* Print descriptive strings for flags rather than the field */ + flags = fmap->areas[i].flags; + if ((str = fmap_flags_to_string(flags)) == NULL) + return -1; + kv_pair_fmt(kv, "area_flags", "%s", str ); + free(str); + + kv_pair_print(kv); + kv_pair_free(kv); + } + + return 0; +} + +/* get SHA1 sum of all static regions described by the flashmap and copy into + *digest (which will be allocated and must be freed by the caller), */ +int fmap_get_csum(const uint8_t *image, unsigned int image_len, uint8_t **digest) +{ + int i; + struct fmap *fmap; + int fmap_offset; + SHA_CTX ctx; + + if ((image == NULL)) + return -1; + + if ((fmap_offset = fmap_find(image, image_len)) < 0) + return -1; + fmap = (struct fmap *)(image + fmap_offset); + + SHA_init(&ctx); + + /* Iterate through flash map and calculate the checksum piece-wise. */ + for (i = 0; i < fmap->nareas; i++) { + /* skip non-static areas */ + if (!(fmap->areas[i].flags & FMAP_AREA_STATIC)) + continue; + + /* sanity check the offset */ + if (fmap->areas[i].size + fmap->areas[i].offset > image_len) { + fprintf(stderr, + "(%s) invalid parameter detected in area %d\n", + __func__, i); + return -1; + } + + SHA_update(&ctx, + image + fmap->areas[i].offset, + fmap->areas[i].size); + } + + SHA_final(&ctx); + *digest = malloc(SHA_DIGEST_SIZE); + memcpy(*digest, ctx.buf, SHA_DIGEST_SIZE); + + return SHA_DIGEST_SIZE; +} + +/* convert raw flags field to user-friendly string */ +char *fmap_flags_to_string(uint16_t flags) +{ + char *str = NULL; + int i, total_size; + + str = malloc(1); + str[0] = '\0'; + total_size = 1; + + for (i = 0; i < sizeof(flags) * CHAR_BIT; i++) { + if (!flags) + break; + + if (flags & (1 << i)) { + const char *tmp = val2str(1 << i, flag_lut); + + total_size += strlen(tmp); + str = realloc(str, total_size); + strcat(str, tmp); + + flags &= ~(1 << i); + if (flags) { + total_size++; + str = realloc(str, total_size); + strcat(str, ","); + } + } + } + + return str; +} + +/* allocate and initialize a new fmap structure */ +struct fmap *fmap_create(uint64_t base, uint32_t size, uint8_t *name) +{ + struct fmap *fmap; + + fmap = malloc(sizeof(*fmap)); + if (!fmap) + return NULL; + + memset(fmap, 0, sizeof(*fmap)); + memcpy(&fmap->signature, FMAP_SIGNATURE, strlen(FMAP_SIGNATURE)); + fmap->ver_major = VERSION_MAJOR; + fmap->ver_minor = VERSION_MINOR; + fmap->base = base; + fmap->size = size; + memccpy(&fmap->name, name, '\0', FMAP_STRLEN); + + return fmap; +} + +/* free memory used by an fmap structure */ +void fmap_destroy(struct fmap *fmap) { + free(fmap); +} + +/* append area to existing structure, return new total size if successful */ +int fmap_append_area(struct fmap **fmap, + uint32_t offset, uint32_t size, + const uint8_t *name, uint16_t flags) +{ + struct fmap_area *area; + int orig_size, new_size; + + if ((fmap == NULL || *fmap == NULL) || (name == NULL)) + return -1; + + /* too many areas */ + if ((*fmap)->nareas >= 0xffff) + return -1; + + orig_size = fmap_size(*fmap); + new_size = orig_size + sizeof(*area); + + *fmap = realloc(*fmap, new_size); + if (*fmap == NULL) + return -1; + + area = (struct fmap_area *)((uint8_t *)*fmap + orig_size); + memset(area, 0, sizeof(*area)); + memcpy(&area->offset, &offset, sizeof(area->offset)); + memcpy(&area->size, &size, sizeof(area->size)); + memccpy(&area->name, name, '\0', FMAP_STRLEN); + memcpy(&area->flags, &flags, sizeof(area->flags)); + + (*fmap)->nareas++; + return new_size; +} + +struct fmap_area *fmap_find_area(struct fmap *fmap, const char *name) +{ + int i; + struct fmap_area *area = NULL; + + if (!fmap || !name) + return NULL; + + for (i = 0; i < fmap->nareas; i++) { + if (!strcmp((const char *)fmap->areas[i].name, name)) { + area = &fmap->areas[i]; + break; + } + } + + return area; +} + +/* + * LCOV_EXCL_START + * Unit testing stuff done here so we do not need to expose static functions. + */ +static enum test_status { pass = EXIT_SUCCESS, fail = EXIT_FAILURE } status; +static struct fmap *fmap_create_test(void) +{ + struct fmap *fmap; + uint64_t base = 0; + uint32_t size = 0x100000; + char name[] = "test_fmap"; + + status = fail; + + fmap = fmap_create(base, size, (uint8_t *)name); + if (!fmap) + return NULL; + + if (memcmp(&fmap->signature, FMAP_SIGNATURE, strlen(FMAP_SIGNATURE))) { + printf("FAILURE: signature is incorrect\n"); + goto fmap_create_test_exit; + } + + if ((fmap->ver_major != VERSION_MAJOR) || + (fmap->ver_minor != VERSION_MINOR)) { + printf("FAILURE: version is incorrect\n"); + goto fmap_create_test_exit; + } + + if (fmap->base != base) { + printf("FAILURE: base is incorrect\n"); + goto fmap_create_test_exit; + } + + if (fmap->size != 0x100000) { + printf("FAILURE: size is incorrect\n"); + goto fmap_create_test_exit; + } + + if (strcmp((char *)fmap->name, "test_fmap")) { + printf("FAILURE: name is incorrect\n"); + goto fmap_create_test_exit; + } + + if (fmap->nareas != 0) { + printf("FAILURE: number of areas is incorrect\n"); + goto fmap_create_test_exit; + } + + status = pass; +fmap_create_test_exit: + /* preserve fmap if all went well */ + if (status == fail) { + fmap_destroy(fmap); + fmap = NULL; + } + return fmap; +} + +static int fmap_print_test(struct fmap *fmap) +{ + return fmap_print(fmap); +} + +static int fmap_get_csum_test(struct fmap *fmap) +{ + uint8_t *digest = NULL, *image = NULL; + /* assume 0x100-0x10100 is marked "static" and is filled with 0x00 */ + int image_size = 0x20000; + uint8_t csum[SHA_DIGEST_SIZE] = { + 0x1a, 0xdc, 0x95, 0xbe, 0xbe, 0x9e, 0xea, 0x8c, + 0x11, 0x2d, 0x40, 0xcd, 0x04, 0xab, 0x7a, 0x8d, + 0x75, 0xc4, 0xf9, 0x61 }; + + status = fail; + + if ((fmap_get_csum(NULL, image_size, &digest) >= 0) || + (fmap_get_csum(image, image_size, NULL) >= 0)) { + printf("failed to abort on NULL pointer input\n"); + goto fmap_get_csum_test_exit; + } + + image = calloc(image_size, 1); + memcpy(image, fmap, fmap_size(fmap)); + + if (fmap_get_csum(image, image_size, &digest) != SHA_DIGEST_SIZE) { + printf("FAILURE: failed to calculate checksum\n"); + goto fmap_get_csum_test_exit; + } + if (memcmp(digest, csum, SHA_DIGEST_SIZE)) { + printf("FAILURE: checksum is incorrect\n"); + goto fmap_get_csum_test_exit; + } + + status = pass; +fmap_get_csum_test_exit: + free(image); + free(digest); + return status; +} + +static int fmap_size_test(struct fmap *fmap) +{ + status = fail; + + if (fmap_size(NULL) >= 0) { + printf("FAILURE: failed to abort on NULL pointer input\n"); + goto fmap_size_test_exit; + } + + status = pass; +fmap_size_test_exit: + return status; +} + +/* this test re-allocates the fmap, so it gets a double-pointer */ +static int fmap_append_area_test(struct fmap **fmap) +{ + int total_size; + uint16_t nareas_orig; + /* test_area will be used by fmap_csum_test and find_area_test */ + struct fmap_area test_area = { + .offset = 0x400, + .size = 0x10000, + .name = "test_area_1", + .flags = FMAP_AREA_STATIC, + }; + + status = fail; + + if ((fmap_append_area(NULL, 0, 0, test_area.name, 0) >= 0) || + (fmap_append_area(fmap, 0, 0, NULL, 0) >= 0)) { + printf("FAILURE: failed to abort on NULL pointer input\n"); + goto fmap_append_area_test_exit; + } + + nareas_orig = (*fmap)->nareas; + (*fmap)->nareas = ~(0); + if (fmap_append_area(fmap, 0, 0, (const uint8_t *)"foo", 0) >= 0) { + printf("FAILURE: failed to abort with too many areas\n"); + goto fmap_append_area_test_exit; + } + (*fmap)->nareas = nareas_orig; + + total_size = sizeof(**fmap) + sizeof(test_area); + if (fmap_append_area(fmap, + test_area.offset, + test_area.size, + test_area.name, + test_area.flags + ) != total_size) { + printf("failed to append area\n"); + goto fmap_append_area_test_exit; + } + + if ((*fmap)->nareas != 1) { + printf("FAILURE: failed to increment number of areas\n"); + goto fmap_append_area_test_exit; + } + + status = pass; +fmap_append_area_test_exit: + return status; +} + +static int fmap_find_area_test(struct fmap *fmap) +{ + status = fail; + char area_name[] = "test_area_1"; + + if (fmap_find_area(NULL, area_name) || + fmap_find_area(fmap, NULL)) { + printf("FAILURE: failed to abort on NULL pointer input\n"); + goto fmap_find_area_test_exit; + } + + if (fmap_find_area(fmap, area_name) == NULL) { + printf("FAILURE: failed to find \"%s\"\n", area_name); + goto fmap_find_area_test_exit; + } + + status = pass; +fmap_find_area_test_exit: + return status; +} + +static int fmap_flags_to_string_test() +{ + char *str, *my_str; + int i; + uint16_t flags; + + status = fail; + + /* no area flag */ + str = fmap_flags_to_string(0); + if (!str || strcmp(str, "")) { + printf("FAILURE: failed to return empty string when no flag" + "are set"); + goto fmap_flags_to_string_test_exit; + } + free(str); + + /* single area flags */ + for (i = 0; i < ARRAY_SIZE(flag_lut); i++) { + if (!flag_lut[i].str) + continue; + + if ((str = fmap_flags_to_string(flag_lut[i].val)) == NULL) { + printf("FAILURE: failed to translate flag to string"); + goto fmap_flags_to_string_test_exit; + } + free(str); + } + + /* construct our own flags field and string using all available flags + * and compare output with fmap_flags_to_string() */ + my_str = calloc(256, 1); + flags = 0; + for (i = 0; i < ARRAY_SIZE(flag_lut); i++) { + if (!flag_lut[i].str) + continue; + else if (i > 0) + strcat(my_str, ","); + + flags |= flag_lut[i].val; + strcat(my_str, flag_lut[i].str); + } + + str = fmap_flags_to_string(flags); + if (strcmp(str, my_str)) { + printf("FAILURE: bad result from fmap_flags_to_string\n"); + goto fmap_flags_to_string_test_exit; + } + free(my_str); + free(str); + + status = pass; +fmap_flags_to_string_test_exit: + return status; + +} + +static int fmap_find_test(struct fmap *fmap) +{ + uint8_t *buf; + size_t total_size, offset; + + status = fail; + + /* + * Note: In these tests, we'll use fmap_find() and control usage of + * lsearch and bsearch by using a power-of-2 total_size. For lsearch, + * use total_size - 1. For bsearch, use total_size. + */ + + total_size = 0x100000; + buf = calloc(total_size, 1); + + /* test if image length is zero */ + if (fmap_find(buf, 0) >= 0) { + printf("FAILURE: failed to abort on zero-length image\n"); + goto fmap_find_test_exit; + } + + /* test if no fmap exists */ + if (fmap_find(buf, total_size - 1) >= 0) { + printf("FAILURE: lsearch returned false positive\n"); + goto fmap_find_test_exit; + } + if (fmap_find(buf, total_size) >= 0) { + printf("FAILURE: bsearch returned false positive\n"); + goto fmap_find_test_exit; + } + + /* simple test case: fmap at (total_size / 2) + 1 */ + offset = (total_size / 2) + 1; + memcpy(&buf[offset], fmap, fmap_size(fmap)); + + if (fmap_find(buf, total_size - 1) != offset) { + printf("FAILURE: lsearch failed to find fmap\n"); + goto fmap_find_test_exit; + } + if (fmap_find(buf, total_size) != offset) { + printf("FAILURE: bsearch failed to find fmap\n"); + goto fmap_find_test_exit; + } + + /* test bsearch if offset is at 0 */ + offset = 0; + memset(buf, 0, total_size); + memcpy(buf, fmap, fmap_size(fmap)); + if (fmap_find(buf, total_size) != offset) { + printf("FAILURE: bsearch failed to find fmap at offset 0\n"); + goto fmap_find_test_exit; + } + + /* test overrun detection */ + memset(buf, 0, total_size); + memcpy(&buf[total_size - fmap_size(fmap) + 1], + fmap, + fmap_size(fmap) + 1); + if (fmap_find(buf, total_size - 1) >= 0) { + printf("FAILURE: lsearch failed to catch overrun\n"); + goto fmap_find_test_exit; + } + if (fmap_find(buf, total_size) >= 0) { + printf("FAILURE: bsearch failed to catch overrun\n"); + goto fmap_find_test_exit; + } + + status = pass; +fmap_find_test_exit: + free(buf); + return status; +} + +int fmap_test() +{ + int rc = EXIT_SUCCESS; + struct fmap *my_fmap; + + /* + * This test has two parts: Creation of an fmap with one or more + * area(s), and other stuff. Since a valid fmap is required to run + * many tests, we abort if fmap creation fails in any way. + * + * Also, fmap_csum_test() makes some assumptions based on the areas + * appended. See fmap_append_area_test() for details. + */ + if ((my_fmap = fmap_create_test()) == NULL) { + rc = EXIT_FAILURE; + goto fmap_test_exit; + } + + if (fmap_find_test(my_fmap)) { + rc = EXIT_FAILURE; + goto fmap_test_exit; + } + + if (fmap_append_area_test(&my_fmap)) { + rc = EXIT_FAILURE; + goto fmap_test_exit; + } + + rc |= fmap_find_area_test(my_fmap); + rc |= fmap_get_csum_test(my_fmap); + rc |= fmap_size_test(my_fmap); + rc |= fmap_flags_to_string_test(); + rc |= fmap_print_test(my_fmap); + +fmap_test_exit: + fmap_destroy(my_fmap); + if (rc) + printf("FAILED\n"); + return rc; +} +/* LCOV_EXCL_STOP */ diff --git a/util/cbfstool/flashmap/fmap.h b/util/cbfstool/flashmap/fmap.h new file mode 100644 index 0000000000..9cea22d37a --- /dev/null +++ b/util/cbfstool/flashmap/fmap.h @@ -0,0 +1,192 @@ +/* + * Copyright 2010, 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. + */ + +#ifndef FLASHMAP_LIB_FMAP_H__ +#define FLASHMAP_LIB_FMAP_H__ + +#include <inttypes.h> + +#include <valstr.h> + +#define FMAP_SIGNATURE "__FMAP__" +#define FMAP_VER_MAJOR 1 /* this header's FMAP minor version */ +#define FMAP_VER_MINOR 1 /* this header's FMAP minor version */ +#define FMAP_STRLEN 32 /* maximum length for strings, */ + /* including null-terminator */ +extern const struct valstr flag_lut[16]; +enum fmap_flags { + FMAP_AREA_STATIC = 1 << 0, + FMAP_AREA_COMPRESSED = 1 << 1, + FMAP_AREA_RO = 1 << 2, +}; + +/* Mapping of volatile and static regions in firmware binary */ +struct fmap_area { + uint32_t offset; /* offset relative to base */ + uint32_t size; /* size in bytes */ + uint8_t name[FMAP_STRLEN]; /* descriptive name */ + uint16_t flags; /* flags for this area */ +} __attribute__((packed)); + +struct fmap { + uint8_t signature[8]; /* "__FMAP__" (0x5F5F464D41505F5F) */ + uint8_t ver_major; /* major version */ + uint8_t ver_minor; /* minor version */ + uint64_t base; /* address of the firmware binary */ + uint32_t size; /* size of firmware binary in bytes */ + uint8_t name[FMAP_STRLEN]; /* name of this firmware binary */ + uint16_t nareas; /* number of areas described by + fmap_areas[] below */ + struct fmap_area areas[]; +} __attribute__((packed)); + +/* + * fmap_find - find FMAP signature in a binary image + * + * @image: binary image + * @len: length of binary image + * + * This function does no error checking. The caller is responsible for + * verifying that the contents are sane. + * + * returns offset of FMAP signature to indicate success + * returns <0 to indicate failure + */ +extern long int fmap_find(const uint8_t *image, unsigned int len); + +/* + * fmap_print - Print contents of flash map data structure + * + * @map: raw map data + * + * returns 0 to indiciate success + * returns <0 to indicate failure + */ +extern int fmap_print(const struct fmap *map); + +/* + * fmap_get_csum - get the checksum of static regions of an image + * + * @image: image to checksum + * @len: length of image + * @digest: double-pointer to store location of first byte of digest + * + * fmap_get_csum() will reset, write, and finalize the digest. + * The location of the final digest will start at the location pointed to + * by digest, which will be allocated and must be freed by the caller. + * + * returns digest length if successful + * returns <0 to indicate error + */ +extern int fmap_get_csum(const uint8_t *image, + unsigned int image_len, uint8_t **digest); + + +/* + * fmap_flags_to_string - convert raw flags field into user-friendly string + * + * @flags: raw flags + * + * This function returns a user-friendly comma-separated list of fmap area + * flags. If there are no flags (flags == 0), the string will contain only + * a terminating character ('\0') + * + * This function allocates memory which the caller must free. + * + * returns pointer to an allocated string if successful + * returns NULL to indicate failure + */ +char *fmap_flags_to_string(uint16_t flags); + +/* + * fmap_create - allocate and initialize a new fmap structure + * + * @base: base address of firmware within address space + * @size: size of the firmware (bytes) + * @name: name of firmware + * + * This function will allocate a flashmap header. Members of the structure + * which are not passed in are automatically initialized. + * + * returns pointer to newly allocated flashmap header if successful + * returns NULL to indicate failure + */ +extern struct fmap *fmap_create(uint64_t base, + uint32_t size, uint8_t *name); + +/* free memory used by an fmap structure */ +extern void fmap_destroy(struct fmap *fmap); + +/* + * fmap_size - returns size of fmap data structure (including areas) + * + * @fmap: fmap + * + * returns size of fmap structure if successful + * returns <0 to indicate failure + */ +extern int fmap_size(struct fmap *fmap); + +/* + * fmap_append_area - realloc an existing flashmap and append an area + * + * @fmap: double pointer to existing flashmap + * @offset: offset of area + * @size: size of area + * @name: name of area + * @flags: area flags + * + * returns total size of reallocated flashmap structure if successful + * returns <0 to indicate failure + */ +extern int fmap_append_area(struct fmap **fmap, + uint32_t offset, uint32_t size, + const uint8_t *name, uint16_t flags); + +/* + * fmap_find_area - find an fmap_area entry (by name) and return pointer to it + * + * @fmap: fmap structure to parse + * @name: name of area to find + * + * returns a pointer to the entry in the fmap structure if successful + * returns NULL to indicate failure or if no matching area entry is found + */ +extern struct fmap_area *fmap_find_area(struct fmap *fmap, const char *name); + +/* unit testing stuff */ +extern int fmap_test(); + +#endif /* FLASHMAP_LIB_FMAP_H__*/ diff --git a/util/cbfstool/flashmap/kv_pair.c b/util/cbfstool/flashmap/kv_pair.c new file mode 100644 index 0000000000..2572c642f8 --- /dev/null +++ b/util/cbfstool/flashmap/kv_pair.c @@ -0,0 +1,242 @@ +/* + * Copyright 2010, 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 <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +#include "kv_pair.h" + +/* Internal variable for output style. Use accessors to get/set style. */ +static enum kv_pair_style _style; + +void kv_pair_set_style(enum kv_pair_style style) +{ + _style = style; +} + +enum kv_pair_style kv_pair_get_style() +{ + return _style; +} + +struct kv_pair *kv_pair_new(void) +{ + struct kv_pair *kv; + + kv = calloc(0, sizeof(*kv)); + if (!kv) + return NULL; + + return kv; +} + +struct kv_pair **kv_pair_new_array(size_t size) +{ + struct kv_pair **kv; + size_t i; + + kv = (struct kv_pair **) calloc(0, sizeof(kv) * size); + for (i = 0; i < size; ++i) { + kv[i] = kv_pair_new(); + } + return kv; +} + +struct kv_pair *kv_pair_add(struct kv_pair *kv_list, + const char *key, const char *value) +{ + struct kv_pair *kv_new; + struct kv_pair *kv_ptr; + + kv_new = kv_pair_new(); + if (!kv_new) + return NULL; + + /* save key=value strings if provided */ + if (key) { + kv_new->key = strdup(key); + if (!kv_new->key) + goto kv_pair_add_failed; + } + if (value) { + kv_new->value = strdup(value); + if (!kv_new->value) + goto kv_pair_add_failed; + } + + /* first in the list if no list provided */ + if (kv_list) { + /* find the end of list */ + for (kv_ptr = kv_list; kv_ptr->next != NULL; + kv_ptr = kv_ptr->next) + ; + + /* link in the new pair at the end */ + kv_ptr->next = kv_new; + } + + /* return pointer to the new pair */ + return kv_new; + +kv_pair_add_failed: + kv_pair_free(kv_new); + return NULL; +} + +struct kv_pair *kv_pair_add_bool(struct kv_pair *kv_list, + const char *key, int value) +{ + const char *str; + + if (value) { + str = "yes"; + } else { + str = "no"; + } + return kv_pair_add(kv_list, key, str); +} + +struct kv_pair *kv_pair_fmt(struct kv_pair *kv_list, + const char *kv_key, const char *format, ...) +{ + char kv_value[KV_PAIR_MAX_VALUE_LEN]; + va_list vptr; + + memset(kv_value, 0, sizeof(kv_value)); + + va_start(vptr, format); + vsnprintf(kv_value, sizeof(kv_value), format, vptr); + va_end(vptr); + + return kv_pair_add(kv_list, kv_key, kv_value); +} + +void kv_pair_free(struct kv_pair *kv_list) +{ + struct kv_pair *kv_ptr = kv_list; + struct kv_pair *kv_next; + + while (kv_ptr != NULL) { + /* free key/value strings */ + if (kv_ptr->key) + free(kv_ptr->key); + if (kv_ptr->value) + free(kv_ptr->value); + + /* free current pair move to next */ + kv_next = kv_ptr->next; + free(kv_ptr); + kv_ptr = kv_next; + } +} + +void kv_pair_free_array(struct kv_pair **kv_array, size_t size) +{ + size_t i; + for (i = 0; i < size; ++i) { + kv_pair_free(kv_array[i]); + } + free(kv_array); +} + +void kv_pair_print_to_file(FILE* fp, struct kv_pair *kv_list, + enum kv_pair_style style) +{ + struct kv_pair *kv_ptr; + + switch (style) { + case KV_STYLE_PAIR: + for (kv_ptr = kv_list; kv_ptr != NULL; kv_ptr = kv_ptr->next) { + if (kv_ptr->key && kv_ptr->value) { + fprintf(fp, "%s=\"%s\" ", + kv_ptr->key, kv_ptr->value); + } + } + break; + + case KV_STYLE_VALUE: + for (kv_ptr = kv_list; kv_ptr != NULL; kv_ptr = kv_ptr->next) { + if (kv_ptr->value) { + fprintf(fp, "%s", kv_ptr->value); + if (kv_ptr->next) + fprintf(fp, " | "); + } + } + break; + + case KV_STYLE_LONG: + for (kv_ptr = kv_list; kv_ptr != NULL; kv_ptr = kv_ptr->next) { + if (kv_ptr->key && kv_ptr->value) + fprintf(fp, "%-20s | %s\n", + kv_ptr->key, kv_ptr->value); + } + break; + } + + fprintf(fp, "\n"); +} + +void kv_pair_print(struct kv_pair *kv_list) +{ + kv_pair_print_to_file(stdout, kv_list, kv_pair_get_style()); +} + +const char *kv_pair_get_value(struct kv_pair *kv_list, const char *kv_key) +{ + const char *kv_value = NULL; + struct kv_pair *kv_ptr; + + for (kv_ptr = kv_list; kv_ptr != NULL; kv_ptr = kv_ptr->next) { + if (kv_ptr->key && strcmp(kv_ptr->key, kv_key) == 0) { + kv_value = kv_ptr->value; + break; + } + } + return kv_value; +} + +int kv_pair_size(struct kv_pair *kv_list) { + struct kv_pair *kv_ptr; + int count; + + count = 0; + for (kv_ptr = kv_list; kv_ptr != NULL; kv_ptr = kv_ptr->next) { + if (kv_ptr->key) { + count++; + } + } + return count; +} diff --git a/util/cbfstool/flashmap/kv_pair.h b/util/cbfstool/flashmap/kv_pair.h new file mode 100644 index 0000000000..b573530c43 --- /dev/null +++ b/util/cbfstool/flashmap/kv_pair.h @@ -0,0 +1,153 @@ +/* + * Copyright 2010, 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. + */ + +#ifndef FLASHMAP_LIB_KV_PAIR_H__ +#define FLASHMAP_LIB_KV_PAIR_H__ + +#include <stdio.h> +#include <inttypes.h> +#include <string.h> + +/* key=value string pair list */ +#define KV_PAIR_MAX_VALUE_LEN 1024 + +enum kv_pair_style { + KV_STYLE_PAIR, /* key1="value1" key2="value2" */ + KV_STYLE_VALUE, /* | value1 | value2 | */ + KV_STYLE_LONG, /* key1 | value1 */ + /* key2 | value2 */ +}; + +struct kv_pair { + char *key; + char *value; + struct kv_pair *next; +}; + +extern enum kv_pair_style kv_pair_get_style(); + +extern void kv_pair_set_style(enum kv_pair_style style); + +/* + * kv_pair_new - create new key=value pair + * + * returns pointer to new key=value pair + * returns NULL to indicate error + */ +extern struct kv_pair *kv_pair_new(void); + +/* + * kv_pair_add - add new key=value pair to list + * + * @kv_list: key=value pair list + * @key: key string + * @value: value string + * + * returns pointer to new key=value pair + * returns NULL to indicate error + */ +extern struct kv_pair *kv_pair_add(struct kv_pair *kv_list, + const char *key, const char *value); + +/* + * kv_pair_add_bool - add new boolean kvpair to list + * + * @kv_list: key=value pair list + * @key: key string + * @value: value + * + * returns pointer to new key=value pair + * returns NULL to indicate error + */ +extern struct kv_pair *kv_pair_add_bool(struct kv_pair *kv_list, + const char *key, int value); + +/* + * kv_pair_fmt - add key=value pair based on printf format + * NOTE: uses variable argument list + * + * @kv_list: list of key=value pairs + * @kv_key: key string + * @format: printf-style format for value input + * @...: arguments to format + * + * returns pointer to new key=value pair + * returns NULL to indicate error + */ +extern struct kv_pair *kv_pair_fmt(struct kv_pair *kv_list, + const char *kv_key, const char *format, ...) + __attribute__((format(printf, 3, 4))); + +/* + * kv_pair_free - clean a key=value pair list + * + * @kv_list: pointer to key=value list + */ +extern void kv_pair_free(struct kv_pair *kv_list); + +/* + * kv_pair_print - print a key=value pair list + * + * @kv_list: pointer to key=value list + * @style: print style + */ +extern void kv_pair_print_to_file(FILE* fp, struct kv_pair *kv_list, + enum kv_pair_style style); + +/* + * kv_pair_print - print a key=value pair list to gsys output + * + * @kv_list: pointer to key=value list + */ +extern void kv_pair_print(struct kv_pair *kv_list); + + +/* + * kv_pair_get_value - return first value with key match + * + * @kv_list: pointer to key=value list + * @kv_key: key string + */ +extern const char *kv_pair_get_value(struct kv_pair *kv_list, + const char *kv_key); + +/* + * kv_pair_size - return number of kv pairs in the chain + * + * @kv_list: pointer to key=value list + */ +extern int kv_pair_size(struct kv_pair *kv_list); + +#endif /* FLASHMAP_LIB_KV_PAIR_H__ */ diff --git a/util/cbfstool/flashmap/valstr.c b/util/cbfstool/flashmap/valstr.c new file mode 100644 index 0000000000..fa9ed82c03 --- /dev/null +++ b/util/cbfstool/flashmap/valstr.c @@ -0,0 +1,65 @@ +/* + * Copyright 2010, 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. + */ + +#include <inttypes.h> +#include <string.h> + +#include <valstr.h> + +const char *val2str_default(uint32_t val, const struct valstr *vs, + const char *def_str) +{ + int i; + + for (i = 0; vs[i].str; i++) { + if (vs[i].val == val) + return vs[i].str; + } + + return def_str; +} + +const char *val2str(uint32_t val, const struct valstr *vs) +{ + return val2str_default(val, vs, "Unknown"); +} + +uint32_t str2val(const char *str, const struct valstr *vs) +{ + int i; + + for (i = 0; vs[i].str; i++) { + if (strcasecmp(vs[i].str, str) == 0) + return vs[i].val; + } + + return vs[i].val; +} diff --git a/util/cbfstool/flashmap/valstr.h b/util/cbfstool/flashmap/valstr.h new file mode 100644 index 0000000000..3885ef75f1 --- /dev/null +++ b/util/cbfstool/flashmap/valstr.h @@ -0,0 +1,78 @@ +/* + * Copyright 2010, 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. + */ + +#ifndef FLASHMAP_LIB_VALSTR_H__ +#define FLASHMAP_LIB_VALSTR_H__ + +#include <inttypes.h> + +/* value + string structure for common conversions */ +struct valstr { + uint32_t val; /* field value */ + const char *str; /* field description */ +}; + +/* + * val2str_default - convert value to string + * + * @val: value to convert + * @vs: value-string data + * @def_str: default string to return if no matching value found + * + * returns pointer to string + * returns def_str if no matching value found + */ +const char *val2str_default(uint32_t val, const struct valstr *vs, + const char *def_str); + +/* + * val2str - convert value to string + * + * @val: value to convert + * @vs: value-string data + * + * returns pointer to string + * returns pointer to "unknown" static string if not found + */ +const char *val2str(uint32_t val, const struct valstr *vs); + +/* + * str2val - convert string to value + * + * @str: string to convert + * @vs: value-string data + * + * returns value for string + * returns value for last entry in value-string data if not found + */ +uint32_t str2val(const char *str, const struct valstr *vs); + +#endif /* FLASHMAP_LIB_VALSTR_H__ */ |