Commit 631ffd1f authored by Alessandro Rubini's avatar Alessandro Rubini

Merge branch 'sdbfs-subdir'

This merges subdirectory support for sdbfs. We are using it in FMC
mezzanine eeproms
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parents 54f73072 6ee4edac
...@@ -369,24 +369,24 @@ The individual fields of the SDB structure are filled in the following way: ...@@ -369,24 +369,24 @@ The individual fields of the SDB structure are filled in the following way:
@item addr_first @item addr_first
For a file this is the first byte address where data is found. For a file this is the first byte address where data is found.
This is currently an absolute address, which makes sense since This is an absolute address for the first level of the tree
we have no subdirectories yet. However, some use cases require and a relative address for subdirectories.
the directory information not to live at offset 0, while referring Unfortunately, some use cases require the top-level
to a file that is at the beginning of the storage. SDB mandates directory information not to live at offset 0, while referring
relative addresses because it's designed for gateware crossbars, to a file that is at the beginning of the storage; thus the
so we'll have to understand how to deal with this when we have use of absolute addresses for the outer directory level.
subdirectories. For the directory itself, addr_first is the For the toplevel directory, addr_first is set to zero
address of the directory -- again, data-before-dir is not cleanly because this is the smallest address used by the tree.
handled.
@itemx addr_last @itemx addr_last
This is the last allocated byte for files. If the configuration This is the last allocated byte for files. If the configuration
file requested extra size for the file, it is accounted here. file requested extra size for the file, it is accounted here.
The trailing space is filled with zeroes by @file{gensdbfs} but The trailing space is filled with zeroes by @file{gensdbfs} but
later the configuration file will be able to specify a filler value. in later releases
For directories, this is the last byte written by any files the configuration file will be able to specify a filler value.
within the directory itself. For directories, this is the last byte encompassed by any files
within the directory itself, including subdirectories.
@item @item
@end table @end table
...@@ -435,7 +435,10 @@ The following options are supported ...@@ -435,7 +435,10 @@ The following options are supported
Specifies the maximum file size. This allows a writeable file to Specifies the maximum file size. This allows a writeable file to
be extended in the filesystem image, for later overwriting with be extended in the filesystem image, for later overwriting with
different data. The program doesn't different data. For directories, this allows reserving space
in your filesystem, that can be filled later -- by another
@i{sdbfs}, whose toplevel directory must live at zero.
The program doesn't
check whether @i{maxsize} is smaller than the current size. check whether @i{maxsize} is smaller than the current size.
@c FIXME: the maxsize/size issue @c FIXME: the maxsize/size issue
...@@ -446,9 +449,10 @@ The following options are supported ...@@ -446,9 +449,10 @@ The following options are supported
you'll find the SDB records, whose first 4 bytes are the magic you'll find the SDB records, whose first 4 bytes are the magic
number. For files this is the placement of the associated data. number. For files this is the placement of the associated data.
For all files where @i{position} is not specified, @file{gensdbfs} For all files where @i{position} is not specified, @file{gensdbfs}
will allocate storage sequentially after the directory itself, will allocate storage sequentially after their own directory listing,
respecting block alignment. It's not possible, currently, to respecting block alignment. It's not possible, currently, to
request all files to be stored sequentially at a specific address request a file to stored sequentially to another file.
The progrma doesn't check for allocation conflicts.
@end table @end table
...@@ -477,6 +481,36 @@ the directory itself at offset 0x1000 and an area of 64kB of storage ...@@ -477,6 +481,36 @@ the directory itself at offset 0x1000 and an area of 64kB of storage
reserved for @code{gensdbfs.c}. The leading part of such are is filled reserved for @code{gensdbfs.c}. The leading part of such are is filled
with the current contents of the file (which is shorter than 64kB). with the current contents of the file (which is shorter than 64kB).
@c --------------------------------------------------------------------------
@node Use-case Example
@subsection Use-case Example
Our first use case for @i{sdbfs} is storing structured data in the 8kB
@sc{eeprom} fo @sc{fmc} cards. The standard reuires the leading part
of the memory to include an @sc{ipmi-fru} data structure, with device
identification, but the rest of the memory can be used at will (the
@sc{fru} structures include the concept of @i{user-area} but it's not
flexible enough.
Memories for our devices, thus, are laid out with @i{sdbfs}: the root
directory lives at address 0x200 and refers to a file called
@t{IPMI-FRU} at address 0. Since most cards reuire calibration
information, such data items are stored in other @sc{sdb} files
in the top directory, where they can be accessed by the device driver
for the specific board -- identified using the @sc{fru} header.
In some cases, the @sc{fpga} driving these cards hosts a @i{White
Rabbit} @sc{ptp} daemon, that needs to access its own information,
such as the @sc{mac} address of the Ethernet port and internal
software-related parameters. To allow this later addition to
@sc{eeprom} contents, the top-level directroy includes a subdirectory,
manually set as @t{position=0x800; size=0x1800}.
When setting up the card for @i{White Rabbit}, the new @i{sdbfs} image
can be written starting at offset 0x800, (2kB), and extending at most
for6kB). The overall filesystem is internally consistent thanks
to the use of relative addresses.
@c ========================================================================== @c ==========================================================================
@node sdb-read @node sdb-read
@section sdb-read @section sdb-read
......
...@@ -20,7 +20,7 @@ OBJDUMP = $(CROSS_COMPILE)objdump ...@@ -20,7 +20,7 @@ OBJDUMP = $(CROSS_COMPILE)objdump
ENDIAN := $(shell ./check-endian $(CC)) ENDIAN := $(shell ./check-endian $(CC))
CFLAGS = -Wall -ggdb -O2 CFLAGS = -Wall -ggdb -O2
CFLAGS += -I../include/linux # for <sdb.h> CFLAGS += -I../include/linux -I../include # for <sdb.h>
CFLAGS += -ffunction-sections -fdata-sections CFLAGS += -ffunction-sections -fdata-sections
CFLAGS += -Wno-pointer-sign CFLAGS += -Wno-pointer-sign
CFLAGS += $(ENDIAN) $(LINUXINCLUDE) CFLAGS += $(ENDIAN) $(LINUXINCLUDE)
......
/* /*
* Copyright (C) 2012 CERN (www.cern.ch) * Copyright (C) 2012,2013 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com> * Author: Alessandro Rubini <rubini@gnudd.com>
* *
* Released according to the GNU GPL, version 2 or any later version. * Released according to the GNU GPL, version 2 or any later version.
...@@ -19,23 +19,42 @@ int sdbfs_fstat(struct sdbfs *fs, struct sdb_device *record_return) ...@@ -19,23 +19,42 @@ int sdbfs_fstat(struct sdbfs *fs, struct sdb_device *record_return)
return 0; return 0;
} }
int sdbfs_fread(struct sdbfs *fs, int offset, char *buf, int count) int sdbfs_fread(struct sdbfs *fs, int offset, void *buf, int count)
{ {
int ret;
if (!fs->currentp) if (!fs->currentp)
return -ENOENT; return -ENOENT;
if (offset < 0) if (offset < 0)
offset = fs->read_offset; offset = fs->read_offset;
if (offset + count > fs->f_len) if (offset + count > fs->f_len)
count = fs->f_len - offset; count = fs->f_len - offset;
ret = count;
if (fs->data) if (fs->data)
memcpy(buf, fs->data + fs->f_offset + offset, count); memcpy(buf, fs->data + fs->f_offset + offset, count);
else else
fs->read(fs, fs->f_offset + offset, buf, count); ret = fs->read(fs, fs->f_offset + offset, buf, count);
fs->read_offset = offset + count; if (ret > 0)
return count; fs->read_offset = offset + ret;
return ret;
} }
int sdbfs_fwrite(struct sdbfs *fs, int offset, char *buf, int count) int sdbfs_fwrite(struct sdbfs *fs, int offset, void *buf, int count)
{ {
return -ENOSYS; int ret;
if (!fs->currentp)
return -ENOENT;
if (offset < 0)
offset = fs->read_offset;
if (offset + count > fs->f_len)
count = fs->f_len - offset;
ret = count;
if (fs->data)
memcpy(buf, fs->data + fs->f_offset + offset, count);
else
ret = fs->write(fs, fs->f_offset + offset, buf, count);
if (ret > 0)
fs->read_offset = offset + ret;
return ret;
} }
...@@ -74,41 +74,97 @@ static struct sdb_device *sdbfs_readentry(struct sdbfs *fs, ...@@ -74,41 +74,97 @@ static struct sdb_device *sdbfs_readentry(struct sdbfs *fs,
*/ */
if (fs->data) if (fs->data)
return (struct sdb_device *)(fs->data + offset); return (struct sdb_device *)(fs->data + offset);
if (!fs->read)
return NULL;
fs->read(fs, offset, &fs->current_record, sizeof(fs->current_record)); fs->read(fs, offset, &fs->current_record, sizeof(fs->current_record));
return &fs->current_record; return &fs->current_record;
} }
/* Helper for scanning: we enter a new directory, and we must validate */
static struct sdb_device *scan_newdir(struct sdbfs *fs, int depth)
{
struct sdb_device *dev;
struct sdb_interconnect *intercon;
dev = fs->currentp = sdbfs_readentry(fs, fs->this[depth]);
if (dev->sdb_component.product.record_type != sdb_type_interconnect)
return NULL;
intercon = (typeof(intercon))dev;
if (ntohl(intercon->sdb_magic) != SDB_MAGIC)
return NULL;
fs->nleft[depth] = ntohs(intercon->sdb_records) - 1;
fs->this[depth] += sizeof(*intercon);
fs->depth = depth;
return dev;
}
struct sdb_device *sdbfs_scan(struct sdbfs *fs, int newscan) struct sdb_device *sdbfs_scan(struct sdbfs *fs, int newscan)
{ {
/* /*
* This returns a pointer to the next sdb record, or a new one. * This returns a pointer to the next sdb record, or the first one.
* Subdirectories are not supported. Uses all internal fields * Subdirectories (bridges) are returned before their contents.
* It only uses internal fields.
*/ */
struct sdb_device *ret; struct sdb_device *dev;
struct sdb_interconnect *i; struct sdb_bridge *bridge;
int depth, type, newdir = 0; /* check there's the magic */
if (newscan) { if (newscan) {
fs->f_offset = fs->entrypoint; fs->base[0] = 0;
} else { fs->this[0] = fs->entrypoint;
fs->f_offset += sizeof(struct sdb_device); depth = fs->depth = 0;
if (!fs->nleft) newdir = 1;
return NULL; goto scan;
} }
ret = sdbfs_readentry(fs, fs->f_offset);
if (newscan) { /* If we already returned a bridge, go inside it (check type) */
i = (typeof(i))ret; depth = fs->depth;
fs->nleft = ntohs(i->sdb_records) - 1; type = fs->currentp->sdb_component.product.record_type;
} else {
fs->nleft--; if (type == sdb_type_bridge && depth + 1 < SDBFS_DEPTH) {
bridge = (typeof(bridge))fs->currentp;
fs->this[depth + 1] = fs->base[depth]
+ ntohll(bridge->sdb_child);
fs->base[depth + 1] = fs->base[depth]
+ ntohll(bridge->sdb_component.addr_first);
depth++;
newdir++;
}
scan:
/* If entering a new directory, verify magic and set nleft */
if (newdir) {
dev = scan_newdir(fs, depth);
if (dev)
return dev;
/* Otherwise the directory is not there: no intercon */
if (!depth)
return NULL; /* no entries at all */
depth--;
} }
return ret;
while (fs->nleft[depth] == 0) {
/* No more at this level, "cd .." if possible */
if (!depth)
return NULL;
fs->depth = --depth;
}
/* so, read the next entry */
dev = fs->currentp = sdbfs_readentry(fs, fs->this[depth]);
fs->this[depth] += sizeof(*dev);
fs->nleft[depth]--;
return dev;
} }
static void __open(struct sdbfs *fs) static void __open(struct sdbfs *fs)
{ {
fs->f_offset = htonll(fs->currentp->sdb_component.addr_first); fs->f_offset = fs->base[fs->depth]
+ htonll(fs->currentp->sdb_component.addr_first);
fs->f_len = htonll(fs->currentp->sdb_component.addr_last) fs->f_len = htonll(fs->currentp->sdb_component.addr_last)
+ 1 - fs->f_offset; + 1 - htonll(fs->currentp->sdb_component.addr_first);
fs->read_offset = 0; fs->read_offset = 0;
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <sdb.h> /* Please point your "-I" to some sensible place */ #include <sdb.h> /* Please point your "-I" to some sensible place */
#define SDBFS_DEPTH 4 /* Max number of subdirectory depth */
/* /*
* Data structures: please not that the library intself doesn't use * Data structures: please not that the library intself doesn't use
* malloc, so it's the caller who must deal withallocation/removal. * malloc, so it's the caller who must deal withallocation/removal.
...@@ -24,7 +25,7 @@ struct sdbfs { ...@@ -24,7 +25,7 @@ struct sdbfs {
/* Some fields are informative */ /* Some fields are informative */
char *name; /* may be null */ char *name; /* may be null */
void *drvdata; /* driver may need some detail.. */ void *drvdata; /* driver may need some detail.. */
int blocksize; unsigned long blocksize;
unsigned long entrypoint; unsigned long entrypoint;
/* The "driver" must offer some methods */ /* The "driver" must offer some methods */
...@@ -34,15 +35,19 @@ struct sdbfs { ...@@ -34,15 +35,19 @@ struct sdbfs {
int (*write)(struct sdbfs *fs, int offset, void *buf, int count); int (*write)(struct sdbfs *fs, int offset, void *buf, int count);
int (*erase)(struct sdbfs *fs, int offset, int count); int (*erase)(struct sdbfs *fs, int offset, int count);
/* The following fields are library-private */ /* All fields from here onwards are library-private */
struct sdb_device current_record;
struct sdb_device *currentp; struct sdb_device *currentp;
int nleft; struct sdb_device current_record;
unsigned long f_offset;
unsigned long f_len; unsigned long f_len;
unsigned long read_offset; unsigned long f_offset; /* start of file */
unsigned long read_offset; /* current location */
unsigned long flags; unsigned long flags;
struct sdbfs *next; struct sdbfs *next;
/* The following ones are directory-aware */
unsigned long base[SDBFS_DEPTH]; /* for relative addresses */
unsigned long this[SDBFS_DEPTH]; /* current sdb record */
int nleft[SDBFS_DEPTH];
int depth;
}; };
#define SDBFS_F_VERBOSE 0x0001 #define SDBFS_F_VERBOSE 0x0001
...@@ -59,8 +64,8 @@ struct sdb_device *sdbfs_scan(struct sdbfs *fs, int newscan); ...@@ -59,8 +64,8 @@ struct sdb_device *sdbfs_scan(struct sdbfs *fs, int newscan);
/* Defined in access.c */ /* Defined in access.c */
int sdbfs_fstat(struct sdbfs *fs, struct sdb_device *record_return); int sdbfs_fstat(struct sdbfs *fs, struct sdb_device *record_return);
int sdbfs_fread(struct sdbfs *fs, int offset, char *buf, int count); int sdbfs_fread(struct sdbfs *fs, int offset, void *buf, int count);
int sdbfs_fwrite(struct sdbfs *fs, int offset, char *buf, int count); int sdbfs_fwrite(struct sdbfs *fs, int offset, void *buf, int count);
/* This is needed to convert endianness. Hoping it is not defined elsewhere */ /* This is needed to convert endianness. Hoping it is not defined elsewhere */
static inline uint64_t htonll(uint64_t ll) static inline uint64_t htonll(uint64_t ll)
......
This diff is collapsed.
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <stdint.h> #include <stdint.h>
#define CFG_NAME "--SDB-CONFIG--" #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*/ /* We need to keep track of each file as both unix and sdb entity*/
struct sdbf { struct sdbf {
...@@ -15,12 +16,14 @@ struct sdbf { ...@@ -15,12 +16,14 @@ struct sdbf {
}; };
char *fullname; char *fullname;
char *basename; char *basename;
unsigned long astart, rstart; /* absolute, relative */ unsigned long ustart, rstart; /* user (mandated), relative */
unsigned long size; unsigned long base, size; /* base is absolute, for output */
int nfiles, totsize; /* for dirs */ int nfiles, totsize; /* for dirs */
struct sdbf *dot; /* for files, pointer to owning dir */ struct sdbf *dot; /* for files, pointer to owning dir */
struct sdbf *parent; /* for dirs, current dir in ../ */ struct sdbf *parent; /* for dirs, current dir in ../ */
int userpos; 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) static inline uint64_t htonll(uint64_t ll)
...@@ -34,4 +37,6 @@ static inline uint64_t htonll(uint64_t ll) ...@@ -34,4 +37,6 @@ static inline uint64_t htonll(uint64_t ll)
return res; return res;
} }
#define ntohll htonll
#endif /* __GENSDBFS_H__ */ #endif /* __GENSDBFS_H__ */
...@@ -38,13 +38,15 @@ static int do_read(struct sdbfs *fs, int offset, void *buf, int count) ...@@ -38,13 +38,15 @@ static int do_read(struct sdbfs *fs, int offset, void *buf, int count)
} }
/* Boring ascii representation of a device */ /* Boring ascii representation of a device */
static void list_device(struct sdb_device *d) static void list_device(struct sdb_device *d, int depth)
{ {
struct sdb_product *p; struct sdb_product *p;
struct sdb_component *c; struct sdb_component *c;
static int warned;
int i;
c = &d->sdb_component; c = &d->sdb_component;
p = &c->product; p = &c->product;
static int warned;
if (!opt_long) { if (!opt_long) {
printf("%.19s\n", p->name); printf("%.19s\n", p->name);
...@@ -57,10 +59,13 @@ static void list_device(struct sdb_device *d) ...@@ -57,10 +59,13 @@ static void list_device(struct sdb_device *d)
warned = 1; warned = 1;
} }
printf("%016llx:%08x @ %08llx-%08llx %.19s\n", /* hack: show directory level looking at the internals */
printf("%016llx:%08x @ %08llx-%08llx ",
ntohll(p->vendor_id), ntohl(p->device_id), ntohll(p->vendor_id), ntohl(p->device_id),
ntohll(c->addr_first), ntohll(c->addr_last), ntohll(c->addr_first), ntohll(c->addr_last));
p->name); for (i = 0; i < depth; i++)
printf(" ");
printf("%.19s\n", p->name);
} }
/* The following three function perform the real work, main() is just glue */ /* The following three function perform the real work, main() is just glue */
...@@ -70,7 +75,7 @@ static void do_list(struct sdbfs *fs) ...@@ -70,7 +75,7 @@ static void do_list(struct sdbfs *fs)
int new = 1; int new = 1;
while ( (d = sdbfs_scan(fs, new)) != NULL) { while ( (d = sdbfs_scan(fs, new)) != NULL) {
list_device(d); list_device(d, fs->depth);
new = 0; new = 0;
} }
} }
......
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