Commit 4efde247 authored by Lucas Russo's avatar Lucas Russo

foreign/libsdbfs/tools: add tools for SDB

This set of tools were taken as is from
http://www.ohwr.org/projects/sdb/repository repository,
written by Alessandro Rubini and Grzegorz Daniluk
parent 1ce15daa
# This is an example config file, that can be used to build a filesystem
# from this very directory. Please note that gensdbfs doesn't look for
# config files in subdirectories but only in the tol-level one.
.
vendor = 0x123456789abcdef
device = 257
position = 0x1000
Makefile
position = 0
gensdbfs.c
write = 1
maxsize = 0x10000
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
CFLAGS = -Wall -ggdb
CFLAGS += -I.. -I../../../include
LDFLAGS = -L.. -lsdbfs
PROG = gensdbfs sdb-read sdb-extract
all: $(PROG)
%: %.c
$(CC) $(CFLAGS) -o $@ $*.c $(LDFLAGS)
$(PROG): ../libsdbfs.a
clean:
rm -f $(PROG) *.o *~ core
# add the other unused targets, so the rule in ../Makefile works
modules install modules_install:
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sdb.h>
#include "gensdbfs.h"
/*
* This takes a directory and turns it into an sdb image. An optional
* config file (called --SDB-CONFIG--) states more about the entries.
* Information about the storage, on the other hand, is received on
* the command line.
*/
/* Lazily, these are globals, pity me */
static unsigned blocksize = 64;
static unsigned long devsize = 0; /* unspecified */
static unsigned long lastwritten = 0;
static char *prgname;
static struct sdbf *prepare_dir(char *name, struct sdbf *parent);
static inline unsigned long SDB_ALIGN(unsigned long x)
{
return (x + (blocksize - 1)) & ~(blocksize - 1);
}
static void __fill_product(struct sdb_product *p, char *name, time_t t,
int record_type)
{
int len = strlen(name);
if (len > sizeof(p->name)) {
fprintf(stderr, "%s: truncating filename \"%s\"\n",
prgname, name);
len = sizeof(p->name);
}
memset(p->name, ' ', sizeof(p->name));
memcpy(p->name, name, len);
memcpy(&p->device_id, p->name, sizeof(p->device_id));
p->vendor_id = DEFAULT_VENDOR; /* changed by config, possibly */
p->version = htonl(1); /* FIXME: version of gensdbfs */
/* FIXME: date */
p->record_type = record_type;
}
/* Helpers for scan_inputdir(), which is below */
static void __fill_dot(struct sdbf *dot, char *dir)
{
struct sdb_interconnect *i = &dot->s_i;
struct sdb_component *c = &i->sdb_component;
struct sdb_product *p = &c->product;
char fn[PATH_MAX];
strcpy(fn, dir);
strcat(fn, "/.");
dot->fullname = strdup(fn);
dot->basename = strdup(".");
i->sdb_magic = htonl(SDB_MAGIC);
i->sdb_version = 1;
i->sdb_bus_type = sdb_data;
/* c->addr_first/last to be filled later */
__fill_product(p, ".", 0 /* date */, sdb_type_interconnect);
}
/* Helper for __fill_file, below, f->stat and f->fullname already valid */
static int __fill_dir(struct sdbf *f)
{
struct sdb_bridge *b = &f->s_b;
struct sdb_component *c = &b->sdb_component;
struct sdb_product *p = &c->product;
/* addr first and last filled later */
__fill_product(p, f->basename, f->stbuf.st_mtime, sdb_type_bridge);
f->subdir = prepare_dir(f->fullname, f->dot);
if (!f->subdir)
return -1;
return 1;
}
static int __fill_file(struct sdbf *f, char *dir, char *fname)
{
char fn[PATH_MAX];
struct sdb_device *d = &f->s_d;
struct sdb_component *c = &d->sdb_component;
struct sdb_product *p = &c->product;
int flags;
strcpy(fn, dir);
strcat(fn, "/");
strcat(fn, fname);
f->fullname = strdup(fn);
f->basename = strdup(fname);
if (stat(fn, &f->stbuf) < 0) {
fprintf(stderr, "%s: stat(%s): %s\n", prgname, fn,
strerror(errno));
return -1;
}
if (S_ISDIR(f->stbuf.st_mode))
return __fill_dir(f);
if (!S_ISREG(f->stbuf.st_mode)) {
fprintf(stderr, "%s: ignoring non-regular \"%s\"\n",
prgname, fn);
return 0;
}
/*
* size can be enlarged by config file, but in any case if the
* file can be written to, align to the block size
*/
f->size = f->stbuf.st_size;
if (f->stbuf.st_mode & S_IWOTH) f->size = SDB_ALIGN(f->size);
/* abi fields remain 0 */
flags = 0;
if (f->stbuf.st_mode & S_IROTH) flags |= SDB_DATA_READ;
if (f->stbuf.st_mode & S_IWOTH) flags |= SDB_DATA_WRITE;
if (f->stbuf.st_mode & S_IXOTH) flags |= SDB_DATA_EXEC;
d->bus_specific = htonl(flags);
/* c->addr_first/last to be filled later */
__fill_product(p, f->basename, f->stbuf.st_mtime, sdb_type_device);
return 1;
}
/* Helpers for scan_config(), which is below */
static struct sdbf *find_filename(struct sdbf *tree, char *s)
{
int i, n = ntohs(tree->s_i.sdb_records);
struct sdbf *f;
for (i = 0; i < n; i++) {
f = tree + i;
if (!strcmp(s, f->basename))
return f;
}
return NULL;
}
static int parse_config_line(struct sdbf *tree, struct sdbf *current, int line,
char *t)
{
struct sdb_device *d = &current->s_d;
struct sdb_component *c = &d->sdb_component;
struct sdb_product *p = &c->product;
unsigned long int32; /* may be 64 bits on some machines */
unsigned long long int64;
int i;
if (getenv("VERBOSE"))
fprintf(stderr, "parse line %i for %s: %s\n", line,
current->fullname, t);
/*
* Unfortunately, scanning as %i refuses "negative" hex values,
* saturating at 0x7fffffff. But %u refuses the leading 0x.
* In order to accept both positive decimal and hex, use %u first,
* and if it returns 0 use %x. I still think hex saturation is a bug.
*/
if (sscanf(t, "vendor = %llu", &int64) == 1) {
if (int64 == 0)
sscanf(t, "vendor = %llx", &int64);
p->vendor_id = htonll(int64);
return 0;
}
if (sscanf(t, "device = %lu", &int32) == 1) {
if (int32 == 0)
sscanf(t, "device = %lx", &int32);
p->device_id = htonl(int32);
return 0;
}
if (sscanf(t, "write = %i", &i) == 1) {
if (i)
d->bus_specific |= htonl(SDB_DATA_WRITE);
else
d->bus_specific &= htonl(~SDB_DATA_WRITE);
return 0;
}
if (sscanf(t, "maxsize = %li", &int32) == 1) {
current->size = int32;
return 0;
}
if (sscanf(t, "position = %li", &int32) == 1) {
if (tree->level != 0) {
fprintf(stderr, "%s: Can't set position in subdirs"
" (file \"%s\")\n", prgname, current->fullname);
return 0;
}
current->userpos = 1;
current->ustart = int32;
return 0;
}
fprintf(stderr, "%s: %s:%i: Unknown directive \"%s\" for file \"%s\"\n",
prgname, CFG_NAME, line, t, current->fullname);
return -1;
}
/* step 0: read the directory and build the tree. Returns NULL on error */
static struct sdbf *scan_inputdir(char *name, struct sdbf *parent, FILE **cfgf)
{
DIR *d;
struct dirent *de;
struct sdbf *tree;
int n, ret;
/* first loop: count the entries */
d = opendir(name);
if (!d) {
fprintf(stderr, "%s: %s: %s\n", prgname, name,
strerror(errno));
return NULL;
}
for (n = 0; (de = readdir(d)); )
n++;
closedir(d);
tree = calloc(n, sizeof(*tree));
if (!tree) {
fprintf(stderr, "%s: out of memory\n", prgname);
return NULL;
}
tree->nfiles = n; /* FIXME: increase this nfile according to cfg */
if (parent)
tree->level = parent->level + 1;
/* second loop: fill it */
d = opendir(name);
if (!d) {
fprintf(stderr, "%s: %s: %s\n", prgname, name,
strerror(errno));
return NULL;
}
for (n = 1; (de = readdir(d)); ) {
/* dot is special: it fills slot 0 */
if (!strcmp(de->d_name, ".")) {
tree[0].de = *de;
__fill_dot(tree, name);
continue;
}
if (!strcmp(de->d_name, ".."))
continue; /* no dot-dot */
if (!strcmp(de->d_name, CFG_NAME)) {
char s[PATH_MAX];
strcpy(s, name);
strcat(s, "/");
strcat(s, de->d_name);
*cfgf = fopen(s, "r");
if (!*cfgf)
fprintf(stderr, "%s: open(%s): %s\n",
prgname, CFG_NAME, strerror(errno));
/* don't exit on this error: proceed without cfg */
continue;
}
tree[n].level = tree[0].level;
tree[n].de = *de;
tree[n].dot = tree;
ret = __fill_file(tree + n, name, de->d_name);
if (ret < 0)
return NULL;
n += ret;
}
/* number or records in the interconnect */
tree->s_i.sdb_records = htons(n);
return tree;
}
static int dumpstruct(FILE *dest, char *name, void *ptr, int size)
{
int ret, i;
unsigned char *p = ptr;
ret = fprintf(dest, "%s (size 0x%x)\n", name, size);
for (i = 0; i < size; ) {
ret += fprintf(dest, "%02x", p[i]);
i++;
ret += fprintf(dest, i & 3 ? " " : i & 0xf ? " " : "\n");
}
if (i & 0xf)
ret += fprintf(dest, "\n");
return ret;
}
static void dump_tree(struct sdbf *tree)
{
int i, n = ntohs(tree->s_i.sdb_records);
for (i = 0; i < n; i++, tree++) {
printf("%s: \"%s\" ino %li\n", tree->fullname, tree->de.d_name,
(long)tree->de.d_ino);
printf("ustart %lx, rstart %lx, base %lx, size %lx (%lx)\n",
tree->ustart, tree->rstart, tree->base, tree->size,
tree->stbuf.st_size);
dumpstruct(stdout, "sdb record", &tree->s_d,
sizeof(tree->s_d));
printf("\n");
}
}
/* step 1: change the in-memory tree according to config file */
static struct sdbf *scan_config(struct sdbf *tree, FILE *f)
{
struct sdbf *current = NULL;
char s[256];
char *t;
int i, lineno = 0;
while (fgets(s, sizeof(s), f)) {
lineno++;
for (i = strlen(s) - 1; i >= 0 && isspace(s[i]); i--)
s[i] = '\0';
t = s;
while (*t && isblank(*t))
t++;
if (*t == '#' || !*t) /* empty or comment */
continue;
if (t == s) {
/* line starts in column 0: new file name */
current = find_filename(tree, s);
if (!current) {
/* FIXME: possibly increase nfile here */
fprintf(stderr, "%s: Warning: %s:%i: "
"\"%s\" not found\n",
prgname, CFG_NAME, lineno, s);
}
continue;
}
if (!current) {
/* ignore directives for non-existent files */
continue;
}
parse_config_line(tree, current, lineno, t);
}
return tree;
}
/* step 2: place the files in the storage area */
static struct sdbf *alloc_storage(struct sdbf *tree)
{
int i, n;
unsigned long subsize;
unsigned long rpos; /* the next expected relative position */
unsigned long l, last; /* keep track of last, for directory record */
struct sdbf *f, *sub;
/* The managed space starts at zero, even if the directory is later */
tree->s_i.sdb_component.addr_first = htonll(0);
/* The "suggested" output place is after the directory itself */
n = ntohs(tree->s_i.sdb_records);
rpos = tree->ustart + SDB_ALIGN(n * sizeof(struct sdb_device));
last = rpos;
for (i = 1; i < n; i++) {
f = tree + i;
/* If a directory, make it allocate itself */
if (f->subdir) {
f->subdir->base = tree->base + rpos;
sub = alloc_storage(f->subdir);
if (!sub) {
fprintf(stderr, "%s: Error allocating %s\n",
prgname, f->fullname);
return NULL;
}
/* this size may have been set by the user */
subsize = ntohll(sub->s_i.sdb_component.addr_last);
if (subsize > f->size)
f->size = subsize;
f->s_b.sdb_child = htonll(rpos);
}
if (f->userpos) { /* user-specified position (level 0) */
f->s_d.sdb_component.addr_first = htonll(f->ustart);
l = f->ustart + f->size - 1;
f->s_d.sdb_component.addr_last = htonll(l);
if (l > last) last = l;
continue;
}
/* position not mandated: go sequential from previous one */
f->rstart = rpos;
f->s_d.sdb_component.addr_first = htonll(rpos);
l = rpos + f->size - 1;
f->s_d.sdb_component.addr_last = htonll(l);
if (l > last) last = l;
if (getenv("VERBOSE"))
fprintf(stderr, "allocated relative %s: %lx to %lx\n",
f->fullname, rpos, l);
rpos = SDB_ALIGN(rpos + f->size);
}
/* finally, save the last used byte for the whole directory */
tree->s_i.sdb_component.addr_last = htonll(last);
return tree;
}
/* step 3: output the image file */
static struct sdbf *write_sdb(struct sdbf *tree, FILE *out)
{
int i, j, n, copied;
unsigned long pos;
struct sdbf *sdbf;
FILE *f;
char *buf;
buf = malloc(blocksize);
if (!buf) {
fprintf(stderr, "%s: out of memory\n", prgname);
return NULL;
}
n = ntohs(tree->s_i.sdb_records);
/*
* First, write the directory, from its possibly user-set position.
* Meanwhile, update base for each of them (used in subdirs)
*/
fseek(out, tree->base + tree->ustart, SEEK_SET);
for (i = 0; i < n; i++) {
fwrite(&tree[i].s_d, sizeof(tree[i].s_d), 1, out);
if (i > 1) /* don't change initial base */
tree[i].base = tree[0].base + tree[i].rstart;
}
if (getenv("VERBOSE")) /* show the user */
dump_tree(tree);
/* then each file */
for (i = 1; i < n; i++) {
sdbf = tree + i;
f = fopen(sdbf->fullname, "r");
if (!f) {
fprintf(stderr, "%s: %s: %s -- ignoring\n", prgname,
sdbf->fullname, strerror(errno));
continue;
}
if (sdbf->userpos) /* only at level 0 */
fseek(out, sdbf->ustart, SEEK_SET);
else
fseek(out, tree->base + sdbf->rstart, SEEK_SET);
if (sdbf->subdir) {
write_sdb(sdbf->subdir, out);
fclose(f);
continue;
}
for (copied = 0; copied < sdbf->stbuf.st_size; ) {
j = fread(buf, 1, blocksize, f);
if (j <= 0)
break; /* unlikely */
fwrite(buf, 1, j, out);
copied += j;
pos = ftell(out);
if (pos > lastwritten)
lastwritten = pos;
}
fclose(f);
}
free(buf);
return tree;
}
/*
* This is the main procedure for each directory, called recursively
* from scan_inputdir() above
*/
static struct sdbf *prepare_dir(char *name, struct sdbf *parent)
{
FILE *fcfg = NULL;
struct sdbf *tree;
/* scan the whole input tree and save the information */
tree = scan_inputdir(name, parent, &fcfg);
if (!tree)
return NULL;
/* read configuration file and save its info for each file */
if (fcfg) {
tree = scan_config(tree, fcfg);
fclose(fcfg);
}
if (!tree)
return NULL;
return tree;
}
/* From now on, it's trivial main program management */
static int usage(char *prgname)
{
fprintf(stderr, "%s: Use \"%s [<options>] <inputdir> <output>\"\n",
prgname, prgname);
fprintf(stderr, " -b <number> : block size (default 64)\n");
fprintf(stderr, " -s <number> : device size (default: as needed)\n");
fprintf(stderr, " a file called \"" CFG_NAME "\", in each "
"subdir is used as configuration file\n");
exit(1);
}
int main(int argc, char **argv)
{
int c;
struct stat stbuf;
FILE *fout;
char *rest;
struct sdbf *tree;
prgname = argv[0];
while ( (c = getopt(argc, argv, "b:s:")) != -1) {
switch (c) {
case 'b':
blocksize = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: not a number \"%s\"\n",
prgname, optarg);
exit(1);
}
break;
case 's':
devsize = strtol(optarg, &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: not a number \"%s\"\n",
prgname, optarg);
exit(1);
}
break;
}
}
if (optind != argc - 2)
usage(prgname);
/* check input and output */
if (stat(argv[optind], &stbuf) < 0) {
fprintf(stderr, "%s: %s: %s\n", prgname, argv[optind],
strerror(errno));
exit(1);
}
if (!S_ISDIR(stbuf.st_mode)) {
/* Recursively fill the directory */
fprintf(stderr, "%s: %s: not a directory\n", prgname,
argv[optind]);
exit(1);
}
fout = fopen(argv[optind+1], "w");
if (!fout) {
fprintf(stderr, "%s: %s: %s\n", prgname, argv[optind+1],
strerror(errno));
exit(1);
}
tree = prepare_dir(argv[optind], NULL /* parent */);
if (!tree)
exit(1);
/* allocate space in the storage */
tree = alloc_storage(tree);
if (!tree)
exit(1);
/* write out the whole tree, recusively */
tree = write_sdb(tree, fout);
if (!tree)
exit(1);
if (lastwritten < devsize) {
fseek(fout, devsize - 1, SEEK_SET);
fwrite("\0", 1, 1, fout);
}
fclose(fout);
if (devsize && (lastwritten > devsize)) {
fprintf(stderr, "%s: data storage (0x%lx) exceeds device size"
" (0x%lx)\n", prgname, lastwritten, devsize);
exit(1);
}
exit(0);
}
#ifndef __GENSDBFS_H__
#define __GENSDBFS_H__
#include <stdint.h>
#define CFG_NAME "--SDB-CONFIG--"
#define DEFAULT_VENDOR htonll(0x46696c6544617461LL) /* "FileData" */
/* We need to keep track of each file as both unix and sdb entity*/
struct sdbf {
struct stat stbuf;
struct dirent de;
union {
struct sdb_device s_d;
struct sdb_interconnect s_i;
struct sdb_bridge s_b;
};
char *fullname;
char *basename;
unsigned long ustart, rstart; /* user (mandated), relative */
unsigned long base, size; /* base is absolute, for output */
int nfiles, totsize; /* for dirs */
struct sdbf *dot; /* for files, pointer to owning dir */
struct sdbf *parent; /* for dirs, current dir in ../ */
struct sdbf *subdir; /* for files that are dirs */
int level; /* subdir level */
int userpos; /* only allowed at level 0 */
};
static inline uint64_t htonll(uint64_t ll)
{
uint64_t res;
if (htonl(1) == 1)
return ll;
res = htonl(ll >> 32);
res |= (uint64_t)(htonl((uint32_t)ll)) << 32;
return res;
}
#define ntohll htonll
#endif /* __GENSDBFS_H__ */
/*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "libsdbfs.h"
#define CFG_NAME "--SDB-CONFIG--"
/*
* This is similar to ./sdb-read, so some code duplication is there,
* but I'd better keep the tools separate and simple
*/
char *prgname;
static int opt_force, opt_entry;
static int create_file(struct sdbfs *fs, struct sdb_device *d, FILE *cfgf)
{
FILE *f;
struct sdb_product *p;
struct sdb_component *c;
char name[32];
int mode = 0444;
c = &d->sdb_component;
p = &c->product;
/* Remove trailing spaces from the name */
strncpy(name, (char *)p->name, sizeof(p->name));
name[sizeof(p->name)] = '\0';
while (name[strlen(name) - 1] == ' ')
name[strlen(name) - 1] = '\0';
/* Print cfgfile information */
fprintf(cfgf, "%s\n" "\tvendor = 0x%016llx\n" "\tdevice = 0x%08x\n",
name, ntohll(p->vendor_id), ntohl(p->device_id));
fprintf(cfgf, "\tposition = 0x%llx\n", ntohll(c->addr_first));
if (ntohl(d->bus_specific) & SDB_DATA_WRITE) {
fprintf(cfgf, "\twrite = 1\n");
mode |= 0222;
}
if (ntohl(d->bus_specific) & SDB_DATA_EXEC)
mode |= 0111;
fprintf(cfgf, "\n");
/* Create the actual file unless it is the root directory */
if (!strcmp(name, "."))
return 0;
f = fopen(name, "w");
if (!f) {
fprintf(stderr, "%s: open(%s): %s\n", prgname, name,
strerror(errno));
return -1;
}
fwrite(fs->data + ntohll(c->addr_first), 1,
ntohll(c->addr_last) + 1 - ntohll(c->addr_first), f);
fclose(f);
chmod(name, mode);
return 0;
}
/* As promised, here's the user-interface glue (and initialization, I admit) */
int main(int argc, char **argv)
{
int n, new, c, err, cfgfd;
FILE *f, *cfgf;
struct sdbfs _fs;
struct sdbfs *fs = &_fs; /* I like to type "fs->" */
struct sdb_device *d;
struct stat stbuf;
void *mapaddr;
char *fsname, *dirname;
struct dirent **namelist;
int i, pagesize = getpagesize();
prgname = argv[0];
while ( (c = getopt(argc, argv, "e:f")) != -1) {
switch (c) {
case 'f':
opt_force = 1;
break;
case 'e':
if (sscanf(optarg, "%i", &opt_entry) != 1) {
fprintf(stderr, "%s: not a number \"%s\"\n",
prgname, optarg);
exit(1);
}
}
}
if (optind != argc - 2) {
fprintf(stderr, "%s: Use: \"%s [-f] [-e <entry>] "
"<output-dir> <sdb-file>\n", prgname, prgname);
exit(1);
}
fsname = argv[optind + 1];
dirname = argv[optind];
if ( !(f = fopen(fsname, "r")) || fstat(fileno(f), &stbuf) < 0) {
fprintf(stderr, "%s: %s: %s\n", prgname, fsname,
strerror(errno));
exit(1);
}
stbuf.st_size += pagesize - 1;
stbuf.st_size &= ~(pagesize - 1);
mapaddr = mmap(0, stbuf.st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0);
if (mapaddr == MAP_FAILED) {
/* I used to complain, but sysfs doesn't allow mmapping... */
mapaddr = malloc(stbuf.st_size);
if (!mapaddr) {
fprintf(stderr, "%s: out of memory reading \"%s\"\n",
argv[0], fsname);
exit(1);
}
i = fread(mapaddr, 1, stbuf.st_size, f);
if (i < stbuf.st_size) {
fprintf(stderr, "%s: %s: short read\n",
argv[0], fsname);
exit(1);
}
}
/* Check output dir is empty, open config file */
/* Open the filesystem */
memset(fs, 0, sizeof(*fs));
fs->name = fsname; /* not mandatory */
fs->blocksize = 256; /* only used for writing, actually */
fs->entrypoint = opt_entry;
fs->data = mapaddr;
err = sdbfs_dev_create(fs);
if (err) {
fprintf(stderr, "%s: sdbfs_dev_create(): %s\n", prgname,
strerror(-err));
fprintf(stderr, "\t(wrong entry point 0x%08lx?)\n",
fs->entrypoint);
exit(1);
}
/* We are sure the fs is good: create output dir and cfgfile */
if (mkdir(dirname, 0777) < 0 && errno != EEXIST) {
fprintf(stderr, "%s: %s: %s\n", prgname, dirname,
strerror(errno));
exit(1);
}
if (chdir(dirname) < 0) {
fprintf(stderr, "%s: %s: %s\n", prgname, dirname,
strerror(errno));
exit(1);
}
n = scandir(".", &namelist, 0, 0);
if (!opt_force && n != 2) {
fprintf(stderr, "%s: %s: not empty\n", prgname, dirname);
exit(1);
}
cfgfd = open(CFG_NAME, O_RDWR | O_CREAT | O_EXCL, 0666);
if (cfgfd < 0) {
fprintf(stderr, "%s: Warning: %s/%s: %s\n", prgname, dirname,
CFG_NAME, strerror(errno));
cfgf = fopen("/dev/null", "w");
} else {
cfgf = fdopen(cfgfd, "w");
}
/* Save the header */
fprintf(cfgf, "# Configuration file generated by %s, reading %s\n\n",
prgname, fsname);
/* The root directory is a file like the other ones */
while ( (d = sdbfs_scan(fs, new)) != NULL) {
create_file(fs, d, cfgf);
new = 0;
}
sdbfs_dev_destroy(fs);
return 0;
}
/*
* Copyright (C) 2012,2014 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* Released according to the GNU GPL, version 2 or any later version.
*
* This work is part of the White Rabbit project, a research effort led
* by CERN, the European Institute for Nuclear Research.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "libsdbfs.h"
char *prgname;
int opt_long, opt_verbose, opt_read, opt_entry, opt_mem;
unsigned long opt_memaddr, opt_memsize;
static void help(void)
{
fprintf(stderr, "%s: Use: \"%s [options] <image-file> [<file>]\n",
prgname, prgname);
fprintf(stderr, " -l long listing (like ls -l)\n");
fprintf(stderr, " -v verbose\n");
fprintf(stderr, " -r force use of read(2), not mmap(2)\n");
fprintf(stderr, " -e <num> entry point offset\n");
fprintf(stderr, " -m <size>@<addr> memory subset to use\n");
fprintf(stderr, " -m <addr>+<size> memory subset to use\n");
exit(1);
}
struct sdbr_drvdata {
void *mapaddr;
FILE *f;
unsigned long memaddr;
unsigned long memsize;
};
/*
* This read method is needed for non-mmappable files, or stuff that
* you can't know the size of (e.g., char devices). You can force use of
* read, to exercise the library procedures, using "-r"
*/
static int do_read(struct sdbfs *fs, int offset, void *buf, int count)
{
struct sdbr_drvdata *drvdata = fs->drvdata;
if (opt_verbose)
fprintf(stderr, "%s @ 0x%08x - size 0x%x (%i)\n", __func__,
offset, count, count);
if (drvdata->mapaddr) {
memcpy(buf, drvdata->mapaddr + offset, count);
return count;
}
/* not mmapped: seek and read */
if (fseek(drvdata->f, drvdata->memaddr + offset, SEEK_SET) < 0)
return -1;
return fread(buf, 1, count, drvdata->f);
}
/* Boring ascii representation of a device */
static int list_device(struct sdb_device *d, int depth, int base)
{
struct sdb_product *p;
struct sdb_component *c;
struct sdb_synthesis *s;
unsigned char *data;
static int warned;
char *warn;
int i, ret;
c = &d->sdb_component;
p = &c->product;
s = (void *)d;
if (!warned && opt_long) {
fprintf(stderr, "%s: listing format is to be defined\n",
prgname);
warned = 1;
}
/* Different sdb items are listed in different ways */
switch(p->record_type) {
/* The following items are components, and are listed as such */
case sdb_type_interconnect:
case sdb_type_device:
case sdb_type_bridge:
if (!opt_long) {
printf("%.19s\n", p->name);
return 0;
}
/* hack: show directory level looking at the internals */
printf("%016llx:%08x @ %08llx-%08llx ",
(long long)ntohll(p->vendor_id), ntohl(p->device_id),
(long long)base + ntohll(c->addr_first),
(long long)base + ntohll(c->addr_last));
for (i = 0; i < depth; i++)
printf(" ");
printf("%.19s\n", p->name);
return 0;
/* A product, but not a component (no address range) */
case sdb_type_integration:
if (!opt_long) {
printf("%.19s\n", p->name);
return 0;
}
printf("%016llx:%08x ",
(long long)ntohll(p->vendor_id), ntohl(p->device_id));
/* like above, show directory level */
for (i = 0; i < depth; i++)
printf(" ");
printf("%.19s\n", p->name);
return 0;
/* Just a string */
case sdb_type_repo_url:
if (opt_long)
printf("repo-url: %.63s\n",
((struct sdb_repo_url *)d)->repo_url);
return 0;
/* Some metadata */
case sdb_type_synthesis:
if (!opt_long)
return 0;
printf("synthesis-name: %.16s\n", s->syn_name);
printf(" commit-id: ");
for (i = 0; i < sizeof(s->commit_id); i++)
printf("%02x", s->commit_id[i]);
printf("\n");
/* Some of the following fields are sometimes empty */
if (s->tool_name[0] && s->tool_name[0] != ' ')
printf(" tool-name: %.8s\n", s->tool_name);
if (s->tool_version)
printf(" tool-version: 0x%08x\n",
ntohl(s->tool_version));
if (s->date)
printf(" build-date: %08x\n", ntohl(s->date));
if (s->user_name[0] && s->tool_name[0] != ' ')
printf(" build-user: %.15s\n", s->user_name);
return 0;
case sdb_type_empty:
return 0;
default:
break;
}
/* Unknown record type */
if (p->record_type & 0x80) {
warn = "Warning";
ret = 0;
} else {
warn = "Error";
ret = -1;
}
fprintf(stderr, "%s: unknown record type 0x%02x\n", warn,
p->record_type);
if (!opt_long) {
printf("Unknown-record\n");
return ret;
}
/* long listing of unknown record */
printf("Unknown-record:\n");
data = (void *)d;
for (i = 0; i < sizeof(struct sdb_empty); i++)
printf("%s%02x%c",
(i & 0xf) == 0 ? " " : "",
data[i],
(i & 0xf) == 0xf ? '\n' : ' ');
return ret;
}
/* The following three function perform the real work, main() is just glue */
static int do_list(struct sdbfs *fs)
{
struct sdb_device *d;
int new = 1;
int err = 0;
while ( (d = sdbfs_scan(fs, new)) != NULL) {
err += list_device(d, fs->depth, fs->base[fs->depth]);
new = 0;
}
return err;
}
static int do_cat_name(struct sdbfs *fs, char *name)
{
char buf[4096];
int i;
i = sdbfs_open_name(fs, name);
if (i < 0) {
fprintf(stderr, "%s: %s: %s\n", prgname, name, strerror(-i));
exit(1);
}
while ( (i = sdbfs_fread(fs, -1, buf, sizeof(buf))) > 0)
fwrite(buf, 1, i, stdout);
sdbfs_close(fs);
return 0;
}
static int do_cat_id(struct sdbfs *fs, uint64_t vendor, uint32_t dev)
{
char buf[4096];
int i;
i = sdbfs_open_id(fs, htonll(vendor), htonl(dev));
if (i < 0) {
fprintf(stderr, "%s: %016llx-%08x: %s\n", prgname,
(long long)vendor, dev, strerror(-i));
exit(1);
}
while ( (i = sdbfs_fread(fs, -1, buf, sizeof(buf))) > 0)
fwrite(buf, 1, i, stdout);
sdbfs_close(fs);
return 0;
}
/* As promised, here's the user-interface glue (and initialization, I admit) */
int main(int argc, char **argv)
{
int c, err;
FILE *f;
struct sdbfs _fs;
struct sdbfs *fs = &_fs; /* I like to type "fs->" */
struct stat stbuf;
struct sdbr_drvdata *drvdata;
void *mapaddr;
char *fsname;
char *filearg = NULL;
unsigned long int32;
unsigned long long int64;
int pagesize = getpagesize();
prgname = argv[0];
while ( (c = getopt(argc, argv, "lvre:m:")) != -1) {
switch (c) {
case 'l':
opt_long = 1;
break;
case 'v':
opt_verbose = 1;
break;
case 'r':
opt_read = 1;
break;
case 'e':
if (sscanf(optarg, "%i", &opt_entry) != 1) {
fprintf(stderr, "%s: not a number \"%s\"\n",
prgname, optarg);
exit(1);
}
break;
case 'm':
/* memory: "size@addr", "addr+size" (blanks ok) */
if (sscanf(optarg, "%li @ %li", &opt_memsize,
&opt_memaddr) == 2)
break;
if (sscanf(optarg, "%li + %li", &opt_memaddr,
&opt_memsize) == 2)
break;
fprintf(stderr, "%s: \"%s\" must be <size>@<addr> "
"or <addr>+<size>\n", prgname, optarg);
exit(1);
break;
}
}
if (optind < argc - 2 || optind > argc - 1)
help();
fsname = argv[optind];
if (optind + 1 < argc)
filearg = argv[optind + 1];
if ( !(f = fopen(fsname, "r")) || fstat(fileno(f), &stbuf) < 0) {
fprintf(stderr, "%s: %s: %s\n", prgname, fsname,
strerror(errno));
exit(1);
}
stbuf.st_size += pagesize - 1;
stbuf.st_size &= ~(pagesize - 1);
mapaddr = mmap(0,
opt_memsize ? opt_memsize : stbuf.st_size,
PROT_READ, MAP_PRIVATE, fileno(f),
opt_memaddr /* 0 by default */);
if (mapaddr == MAP_FAILED)
mapaddr = NULL; /* We'll seek/read */
/* So, describe the filesystem instance and give it to the library */
memset(fs, 0, sizeof(*fs));
drvdata = calloc(1, sizeof(*drvdata));
if (!drvdata) {perror("malloc"); exit(1);}
drvdata->f = f;
drvdata->memaddr = opt_memaddr;
drvdata->memsize = opt_memsize;
drvdata->mapaddr = mapaddr;
fs->drvdata = drvdata;
fs->name = fsname; /* not mandatory */
fs->blocksize = 256; /* only used for writing, actually */
fs->entrypoint = opt_entry;
if (opt_read || !drvdata->mapaddr)
fs->read = do_read;
else
fs->data = mapaddr;
if (opt_verbose)
fs->flags |= SDBFS_F_VERBOSE;
err = sdbfs_dev_create(fs);
if (err) {
fprintf(stderr, "%s: sdbfs_dev_create(): %s\n", prgname,
strerror(-err));
fprintf(stderr, "\t(wrong entry point 0x%08lx?)\n",
fs->entrypoint);
exit(1);
}
/* Now use the thing: either scan, or look for name, or look for id */
if (!filearg)
err = do_list(fs);
else if (sscanf(filearg, "%llx:%lx", &int64, &int32) != 2)
err = do_cat_name(fs, filearg);
else
err = do_cat_id(fs, int64, int32);
sdbfs_dev_destroy(fs);
return err;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment