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:
@item addr_first
For a file this is the first byte address where data is found.
This is currently an absolute address, which makes sense since
we have no subdirectories yet. However, some use cases require
the directory information not to live at offset 0, while referring
to a file that is at the beginning of the storage. SDB mandates
relative addresses because it's designed for gateware crossbars,
so we'll have to understand how to deal with this when we have
subdirectories. For the directory itself, addr_first is the
address of the directory -- again, data-before-dir is not cleanly
handled.
This is an absolute address for the first level of the tree
and a relative address for subdirectories.
Unfortunately, some use cases require the top-level
directory information not to live at offset 0, while referring
to a file that is at the beginning of the storage; thus the
use of absolute addresses for the outer directory level.
For the toplevel directory, addr_first is set to zero
because this is the smallest address used by the tree.
@itemx addr_last
This is the last allocated byte for files. If the configuration
file requested extra size for the file, it is accounted here.
The trailing space is filled with zeroes by @file{gensdbfs} but
later the configuration file will be able to specify a filler value.
For directories, this is the last byte written by any files
within the directory itself.
in later releases
the configuration file will be able to specify a filler value.
For directories, this is the last byte encompassed by any files
within the directory itself, including subdirectories.
@item
@end table
......@@ -435,7 +435,10 @@ The following options are supported
Specifies the maximum file size. This allows a writeable file to
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.
@c FIXME: the maxsize/size issue
......@@ -446,9 +449,10 @@ The following options are supported
you'll find the SDB records, whose first 4 bytes are the magic
number. For files this is the placement of the associated data.
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
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
......@@ -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
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 ==========================================================================
@node sdb-read
@section sdb-read
......
......@@ -20,7 +20,7 @@ OBJDUMP = $(CROSS_COMPILE)objdump
ENDIAN := $(shell ./check-endian $(CC))
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 += -Wno-pointer-sign
CFLAGS += $(ENDIAN) $(LINUXINCLUDE)
......
/*
* Copyright (C) 2012 CERN (www.cern.ch)
* Copyright (C) 2012,2013 CERN (www.cern.ch)
* Author: Alessandro Rubini <rubini@gnudd.com>
*
* 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)
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)
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
fs->read(fs, fs->f_offset + offset, buf, count);
fs->read_offset = offset + count;
return count;
ret = fs->read(fs, fs->f_offset + offset, buf, count);
if (ret > 0)
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,
*/
if (fs->data)
return (struct sdb_device *)(fs->data + offset);
if (!fs->read)
return NULL;
fs->read(fs, offset, &fs->current_record, sizeof(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)
{
/*
* This returns a pointer to the next sdb record, or a new one.
* Subdirectories are not supported. Uses all internal fields
* This returns a pointer to the next sdb record, or the first one.
* Subdirectories (bridges) are returned before their contents.
* It only uses internal fields.
*/
struct sdb_device *ret;
struct sdb_interconnect *i;
struct sdb_device *dev;
struct sdb_bridge *bridge;
int depth, type, newdir = 0; /* check there's the magic */
if (newscan) {
fs->f_offset = fs->entrypoint;
} else {
fs->f_offset += sizeof(struct sdb_device);
if (!fs->nleft)
return NULL;
fs->base[0] = 0;
fs->this[0] = fs->entrypoint;
depth = fs->depth = 0;
newdir = 1;
goto scan;
}
ret = sdbfs_readentry(fs, fs->f_offset);
if (newscan) {
i = (typeof(i))ret;
fs->nleft = ntohs(i->sdb_records) - 1;
} else {
fs->nleft--;
/* If we already returned a bridge, go inside it (check type) */
depth = fs->depth;
type = fs->currentp->sdb_component.product.record_type;
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)
{
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)
+ 1 - fs->f_offset;
+ 1 - htonll(fs->currentp->sdb_component.addr_first);
fs->read_offset = 0;
}
......
......@@ -12,6 +12,7 @@
#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
* malloc, so it's the caller who must deal withallocation/removal.
......@@ -24,7 +25,7 @@ struct sdbfs {
/* Some fields are informative */
char *name; /* may be null */
void *drvdata; /* driver may need some detail.. */
int blocksize;
unsigned long blocksize;
unsigned long entrypoint;
/* The "driver" must offer some methods */
......@@ -34,15 +35,19 @@ struct sdbfs {
int (*write)(struct sdbfs *fs, int offset, void *buf, int count);
int (*erase)(struct sdbfs *fs, int offset, int count);
/* The following fields are library-private */
struct sdb_device current_record;
/* All fields from here onwards are library-private */
struct sdb_device *currentp;
int nleft;
unsigned long f_offset;
struct sdb_device current_record;
unsigned long f_len;
unsigned long read_offset;
unsigned long f_offset; /* start of file */
unsigned long read_offset; /* current location */
unsigned long flags;
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
......@@ -59,8 +64,8 @@ struct sdb_device *sdbfs_scan(struct sdbfs *fs, int newscan);
/* Defined in access.c */
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_fwrite(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, void *buf, int count);
/* This is needed to convert endianness. Hoping it is not defined elsewhere */
static inline uint64_t htonll(uint64_t ll)
......
This diff is collapsed.
......@@ -3,6 +3,7 @@
#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 {
......@@ -15,12 +16,14 @@ struct sdbf {
};
char *fullname;
char *basename;
unsigned long astart, rstart; /* absolute, relative */
unsigned long size;
int nfiles, totsize; /* for dirs */
struct sdbf *dot; /* for files, pointer to owning dir */
struct sdbf *parent; /* for dirs, current dir in ../ */
int userpos;
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)
......@@ -34,4 +37,6 @@ static inline uint64_t htonll(uint64_t ll)
return res;
}
#define ntohll htonll
#endif /* __GENSDBFS_H__ */
......@@ -38,13 +38,15 @@ static int do_read(struct sdbfs *fs, int offset, void *buf, int count)
}
/* 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_component *c;
static int warned;
int i;
c = &d->sdb_component;
p = &c->product;
static int warned;
if (!opt_long) {
printf("%.19s\n", p->name);
......@@ -57,10 +59,13 @@ static void list_device(struct sdb_device *d)
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(c->addr_first), ntohll(c->addr_last),
p->name);
ntohll(c->addr_first), ntohll(c->addr_last));
for (i = 0; i < depth; i++)
printf(" ");
printf("%.19s\n", p->name);
}
/* The following three function perform the real work, main() is just glue */
......@@ -70,7 +75,7 @@ static void do_list(struct sdbfs *fs)
int new = 1;
while ( (d = sdbfs_scan(fs, new)) != NULL) {
list_device(d);
list_device(d, fs->depth);
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