blob: ceaa85ff06c3c04d69a2eed917a8fed3d671e2c2 [file] [log] [blame]
/*
* Copyright (c) International Business Machines Corp., 2006, 2007
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Authors: Drake Dowsett, dowsett@de.ibm.com
* Contact: Andreas Arnez, arnez@de.ibm.com
*
* unubi uses the following functions to generate analysis output based on
* the header information in a raw-UBI image
*/
/*
* TODO: use OOB data to check for eraseblock validity in NAND images
*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <mtd_swab.h>
#include "unubi_analyze.h"
#include "crc32.h"
#define EC_X_INT 50
/**
* intcmp - function needed by qsort to order integers
**/
int intcmp(const void *a, const void *b)
{
int A = *(int *)a;
int B = *(int *)b;
return A - B;
}
int longcmp(const void *a, const void *b)
{
long long A = *(long long *)a;
long long B = *(long long *)b;
return A - B;
}
/**
* unubi_analyze_group_index - finds the normalized index in an array
* item: look for this item in the array
* array: array to search through
* size: length of the array
* array should be sorted for this algorithm to perform properly;
* if the item is not found returns -1, otherwise return value is the
* index in the array (note this contricts the array size to 2^32-1);
**/
int
norm_index(uint32_t item, uint32_t *array, size_t length)
{
size_t i, index;
for (index = 0, i = 0; i < length; i++) {
if ((i != 0) && (array[i] != array[i - 1]))
index++;
if (item == array[i])
return index;
}
return -1;
}
/**
* unubi_analyze_ec_hdr - generate data table and plot script
* first: head of simple linked list
* path: folder to write into
* generates a data file containing the eraseblock index in the image
* and the erase counter found in its ec header;
* if the crc check fails, the line is commented out in the data file;
* also generates a simple gnuplot sript for quickly viewing one
* display of the data file;
**/
int
unubi_analyze_ec_hdr(struct eb_info *first, const char *path)
{
char filename[PATH_MAX + 1];
size_t count, eraseblocks;
uint32_t crc, crc32_table[256];
uint64_t *erase_counts;
FILE* fpdata;
FILE* fpplot;
struct eb_info *cur;
if (first == NULL)
return -1;
/* crc check still needed for `first' linked list */
init_crc32_table(crc32_table);
/* prepare output files */
memset(filename, 0, PATH_MAX + 1);
snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_DATA);
fpdata = fopen(filename, "w");
if (fpdata == NULL)
return -1;
memset(filename, 0, PATH_MAX + 1);
snprintf(filename, PATH_MAX, "%s/%s", path, FN_EH_PLOT);
fpplot = fopen(filename, "w");
if (fpplot == NULL) {
fclose(fpdata);
return -1;
}
/* make executable */
chmod(filename, 0755);
/* first run: count elements */
count = 0;
cur = first;
while (cur != NULL) {
cur = cur->next;
count++;
}
eraseblocks = count;
erase_counts = malloc(eraseblocks * sizeof(*erase_counts));
if (!erase_counts) {
perror("out of memory");
exit(EXIT_FAILURE);
}
memset(erase_counts, 0, eraseblocks * sizeof(*erase_counts));
/* second run: populate array to sort */
count = 0;
cur = first;
while (cur != NULL) {
erase_counts[count] = be64_to_cpu(cur->ec.ec);
cur = cur->next;
count++;
}
qsort(erase_counts, eraseblocks, sizeof(*erase_counts),
(void *)longcmp);
/* third run: generate data file */
count = 0;
cur = first;
fprintf(fpdata, "# eraseblock_no actual_erase_count "
"sorted_erase_count\n");
while (cur != NULL) {
crc = clc_crc32(crc32_table, UBI_CRC32_INIT, &cur->ec,
UBI_EC_HDR_SIZE_CRC);
if ((be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC) ||
(crc != be32_to_cpu(cur->ec.hdr_crc)))
fprintf(fpdata, "# ");
fprintf(fpdata, "%zu %llu %llu", count,
(unsigned long long)be64_to_cpu(cur->ec.ec),
(unsigned long long)erase_counts[count]);
if (be32_to_cpu(cur->ec.magic) != UBI_EC_HDR_MAGIC)
fprintf(fpdata, " ## bad magic: %08x",
be32_to_cpu(cur->ec.magic));
if (crc != be32_to_cpu(cur->ec.hdr_crc))
fprintf(fpdata, " ## CRC mismatch: given=%08x, "
"calc=%08x", be32_to_cpu(cur->ec.hdr_crc),
crc);
fprintf(fpdata, "\n");
cur = cur->next;
count++;
}
fclose(fpdata);
fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n");
fprintf(fpplot, "set xlabel \"eraseblock\"\n");
/* fourth run: generate plot file xtics */
count = 0;
cur = first;
fprintf(fpplot, "set xtics (");
while (cur != NULL) {
if ((count % EC_X_INT) == 0) {
if (count > 0)
fprintf(fpplot, ", ");
fprintf(fpplot, "%zd", count);
}
cur = cur->next;
count++;
}
fprintf(fpplot, ")\n");
fprintf(fpplot, "set ylabel \"erase count\"\n");
fprintf(fpplot, "set xrange [-1:%zu]\n", eraseblocks + 1);
fprintf(fpplot, "# set yrange [-1:%llu]\n",
(unsigned long long)erase_counts[eraseblocks - 1] + 1);
fprintf(fpplot, "plot \"%s\" u 1:2 t \"unsorted: %s\" with boxes\n",
FN_EH_DATA, FN_EH_DATA);
fprintf(fpplot, "# replot \"%s\" u 1:3 t \"sorted: %s\" with lines\n",
FN_EH_DATA, FN_EH_DATA);
fprintf(fpplot, "pause -1 \"press ENTER\"\n");
fclose(fpplot);
return 0;
}
/**
* unubi_analyze_vid_hdr - generate data table and plot script
* head: head of complex linked list (eb_chain)
* path: folder to write into
* generates a data file containing the volume id, logical number, leb version,
* and data size from the vid header;
* all eraseblocks listed in the eb_chain are valid (checked in unubi);
* also generates a simple gnuplot sript for quickly viewing one
* display of the data file;
**/
int
unubi_analyze_vid_hdr(struct eb_info **head, const char *path)
{
char filename[PATH_MAX + 1];
int rc, y1, y2;
size_t count, step, breadth;
uint32_t *leb_versions, *data_sizes;
FILE* fpdata;
FILE* fpplot;
struct eb_info *cur;
if (head == NULL || *head == NULL)
return -1;
rc = 0;
fpdata = NULL;
fpplot = NULL;
data_sizes = NULL;
leb_versions = NULL;
/* prepare output files */
memset(filename, 0, PATH_MAX + 1);
snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_DATA);
fpdata = fopen(filename, "w");
if (fpdata == NULL) {
rc = -1;
goto exit;
}
memset(filename, 0, PATH_MAX + 1);
snprintf(filename, PATH_MAX, "%s/%s", path, FN_VH_PLOT);
fpplot = fopen(filename, "w");
if (fpplot == NULL) {
rc = -1;
goto exit;
}
/* make executable */
chmod(filename, 0755);
/* first run: count elements */
count = 0;
cur = *head;
while (cur != NULL) {
cur = cur->next;
count++;
}
breadth = count;
leb_versions = malloc(breadth * sizeof(uint32_t));
if (leb_versions == NULL) {
rc = -1;
goto exit;
}
memset(leb_versions, 0, breadth * sizeof(uint32_t));
data_sizes = malloc(breadth * sizeof(uint32_t));
if (data_sizes == NULL) {
rc = -1;
goto exit;
}
memset(data_sizes, 0, breadth * sizeof(*data_sizes));
/* second run: populate arrays to sort */
count = 0;
cur = *head;
while (cur != NULL) {
leb_versions[count] = be32_to_cpu(cur->vid.leb_ver);
data_sizes[count] = be32_to_cpu(cur->vid.data_size);
cur = cur->next;
count++;
}
qsort(leb_versions, breadth, sizeof(*leb_versions), (void *)intcmp);
qsort(data_sizes, breadth, sizeof(*data_sizes), (void *)intcmp);
/* third run: generate data file */
count = 0;
cur = *head;
fprintf(fpdata, "# x_axis vol_id lnum y1_axis leb_ver "
"y2_axis data_size\n");
while (cur != NULL) {
y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions,
breadth);
y2 = norm_index(be32_to_cpu(cur->vid.data_size), data_sizes,
breadth);
if ((y1 == -1) || (y2 == -1)) {
rc = -1;
goto exit;
}
fprintf(fpdata, "%zu %u %u %u %u %u %u\n",
count,
be32_to_cpu(cur->vid.vol_id),
be32_to_cpu(cur->vid.lnum),
y1,
be32_to_cpu(cur->vid.leb_ver),
y2,
be32_to_cpu(cur->vid.data_size));
cur = cur->next;
count++;
}
fprintf(fpplot, "#!/usr/bin/gnuplot -persist\n");
fprintf(fpplot, "set xlabel \"volume\"\n");
/* fourth run: generate plot file xtics */
count = 0;
step = 0;
cur = *head;
fprintf(fpplot, "set xtics (");
while (cur != NULL) {
if (count > 0)
fprintf(fpplot, ", ");
if (step != be32_to_cpu(cur->vid.vol_id)) {
step = be32_to_cpu(cur->vid.vol_id);
fprintf(fpplot, "\"%zd\" %zd 0", step, count);
}
else
fprintf(fpplot, "\"%d\" %zd 1",
be32_to_cpu(cur->vid.lnum), count);
cur = cur->next;
count++;
}
fprintf(fpplot, ")\n");
fprintf(fpplot, "set nox2tics\n");
/* fifth run: generate plot file ytics */
count = 0;
cur = *head;
fprintf(fpplot, "set ylabel \"leb version\"\n");
fprintf(fpplot, "set ytics (");
while (cur->next != NULL) {
y1 = norm_index(be32_to_cpu(cur->vid.leb_ver), leb_versions,
breadth);
if (y1 == -1) {
rc = -1;
goto exit;
}
if (count > 0)
fprintf(fpplot, ", ");
fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.leb_ver),
y1);
cur = cur->next;
count++;
}
fprintf(fpplot, ")\n");
/* sixth run: generate plot file y2tics */
count = 0;
cur = *head;
fprintf(fpplot, "set y2label \"data size\"\n");
fprintf(fpplot, "set y2tics (");
while (cur != NULL) {
y2 = norm_index(be32_to_cpu(cur->vid.data_size),
data_sizes, breadth);
if (y2 == -1) {
rc = -1;
goto exit;
}
if (count > 0)
fprintf(fpplot, ", ");
fprintf(fpplot, "\"%u\" %u", be32_to_cpu(cur->vid.data_size),
y2);
cur = cur->next;
count++;
}
fprintf(fpplot, ")\n");
y1 = norm_index(leb_versions[breadth - 1], leb_versions, breadth);
y2 = norm_index(data_sizes[breadth - 1], data_sizes, breadth);
fprintf(fpplot, "set xrange [-1:%zu]\n", count + 1);
fprintf(fpplot, "set yrange [-1:%u]\n", y1 + 1);
fprintf(fpplot, "set y2range [-1:%u]\n", y2 + 1);
fprintf(fpplot, "plot \"%s\" u 1:4 t \"leb version: %s\" "
"axes x1y1 with lp\n", FN_VH_DATA, FN_VH_DATA);
fprintf(fpplot, "replot \"%s\" u 1:6 t \"data size: %s\" "
"axes x1y2 with lp\n", FN_VH_DATA, FN_VH_DATA);
fprintf(fpplot, "pause -1 \"press ENTER\"\n");
exit:
if (fpdata != NULL)
fclose(fpdata);
if (fpplot != NULL)
fclose(fpplot);
if (data_sizes != NULL)
free(data_sizes);
if (leb_versions != NULL)
free(leb_versions);
return rc;
}
/**
* unubi_analyze - run all analyses
* head: eb_chain head
* first: simple linked list of eraseblock headers (use .next)
* path: directory (without trailing slash) to output to
* returns 0 upon successful completion, or -1 otherwise
**/
int
unubi_analyze(struct eb_info **head, struct eb_info *first, const char *path)
{
int ec_rc, vid_rc;
if (path == NULL)
return -1;
ec_rc = unubi_analyze_ec_hdr(first, path);
vid_rc = unubi_analyze_vid_hdr(head, path);
if (ec_rc < 0 || vid_rc < 0)
return -1;
return 0;
}