Commit a0329e62 authored by Alessandro Rubini's avatar Alessandro Rubini

Merge branch 'sdbfs-gitorious'

This is a merge of all previous work done on sdbfs using a different
repository (which I used to push to gitorious).

Besides the kernel stuff, which is still in flush, userspace and lib
are actually useful to sdb users in general, so this is a better place
to host it.
parents b99c61e4 4da49f56
*~
*.o
*.a
*.mod.c
*.ko
.*.cmd
modules.order
.tmp_versions
Module.symvers
This diff is collapsed.
DIRS = lib userspace kernel doc
all clean modules install modules_install:
for d in $(DIRS); do $(MAKE) -C $$d $@ || exit 1; done
This is an implementation of the SDB filesystem. Such thing is a
flash-based filesystem meant to be accessed both by a soft-core
running on the FPGA and the host computer (but not at the same time).
The structure of the filesystem is read-only, but each individual file
may be read-write, based on alignment restrictions of the hosting
device.
The thing is split in three parts
kernel/
includes the kernel driver (filesystem and sample hw support).
lib/
includes the library that the soft-core can use to access its
own flash memory.
userspace/
includes the gensdbfs tool and other user-space stuff that uses
the library above.
Since SDB is used to self-describe a bus, you can even mount sdbfs
over a real bus. In this case however only mmap is allowed on the
individual files (no read or write method is there).
*~
*.aux
*.cp
*.cps
*.fn
*.html
*.info
*.ky
*.log
*.pdf
*.pg
*.texi
*.toc
*.tp
/*.txt
*.vr
#
# Makefile for the documentation directory
#
# Copyright 1994,2000,2010,2011 Alessandro Rubini <rubini@linux.it>
#
#################
# There is not basenames here, all *.in are considered input
INPUT = $(wildcard *.in)
TEXI = $(INPUT:.in=.texi)
INFO = $(INPUT:.in=.info)
HTML = $(INPUT:.in=.html)
TXT = $(INPUT:.in=.txt)
PDF = $(INPUT:.in=.pdf)
ALL = $(INFO) $(HTML) $(TXT) $(PDF)
MAKEINFO ?= makeinfo
%.texi: %.in
@rm -f $@
sed -f ./infofilter $< > $@
emacs -batch --no-site-file -l fixinfo $@
chmod -w $@
%.pdf: %.texi
texi2pdf --batch $<
%.info: %.texi
$(MAKEINFO) $< -o $@
%.html: %.texi
$(MAKEINFO) --html --no-split -o $@ $<
%.txt: %.texi
$(MAKEINFO) --no-headers $< > $@
##############################################
.PHONY: all images check terse clean install
.INTERMEDIATE: $(TEXI)
all: images $(ALL)
$(MAKE) terse
images::
if [ -d images ]; then $(MAKE) -C images || exit 1; fi
check: _err.ps
gs -sDEVICE=linux -r320x200x16 $<
terse:
for n in cp fn ky pg toc tp vr aux log; do rm -f *.$$n; done
rm -f *~
clean: terse
rm -f $(ALL) $(TEXI)
# add the other unused targets, so the rule in ../Makefile works
modules install modules_install:
;; use:
;; emacs -batch -l ./fixinfo.el <file>
;; or, better:
;; emacs -batch --no-site-file -l ./fixinfo.el <file>
(defun fixinfo (file)
(find-file-other-window file)
(message (concat "Maxing texinfo tree in " file))
(texinfo-all-menus-update)
(texinfo-every-node-update)
(save-buffer)
(kill-buffer (current-buffer))
)
;; loop over command line arguments
(mapcar 'fixinfo command-line-args-left)
(kill-emacs)
#! /usr/bin/sed -f
# allow "%" as a comment char, but only at the beginning of the line
s/^%/@c /
#s/[^\\]%.*$//
s/^\\%/%/
#preserve blanks and braces in @example blocks
/^@example/,/^@end example/ s/{/@{/g
/^@example/,/^@end example/ s/}/@}/g
/^@example/,/^@end example/ p
/^@example/,/^@end example/ d
/^@smallexample/,/^@end smallexample/ s/{/@{/g
/^@smallexample/,/^@end smallexample/ s/}/@}/g
/^@smallexample/,/^@end smallexample/ p
/^@smallexample/,/^@end smallexample/ d
# remove leading blanks
s/^[ ]*//
This diff is collapsed.
/*
* This is a modified version 1.0 of sdb.h, not the official spec version
*/
#ifndef __SDB_H__
#define __SDB_H__
#ifdef __KERNEL__
#include <linux/types.h>
#else
#include <stdint.h>
#endif
/*
* All structures are 64 bytes long and are expected
* to live in an array, one for each interconnect.
* Most fields of the structures are shared among the
* various types, and most-specific fields are at the
* beginning (for alignment reasons, and to keep the
* magic number at the head of the interconnect record
*/
/* Product, 40 bytes at offset 24, 8-byte alignmed
*
* device_id is vendor-assigned; version is device-specific,
* date is hex (e.g 0x20120501), name is UTF-8, blank-filled
* and not terminated with a 0 byte.
*/
struct sdb_product {
uint64_t vendor_id; /* 0x18..0x1f */
uint32_t device_id; /* 0x20..0x23 */
uint32_t version; /* 0x24..0x27 */
uint32_t date; /* 0x28..0x2b */
uint8_t name[19]; /* 0x2c..0x3e */
uint8_t record_type; /* 0x3f */
};
/*
* Component, 56 bytes at offset 8, 8-byte aligned
*
* The address range is first to last, inclusive
* (for example 0x100000 - 0x10ffff)
*/
struct sdb_component {
uint64_t addr_first; /* 0x08..0x0f */
uint64_t addr_last; /* 0x10..0x17 */
struct sdb_product product; /* 0x18..0x3f */
};
/* Type of the SDB record */
enum sdb_record_type {
sdb_type_interconnect = 0x00,
sdb_type_device = 0x01,
sdb_type_bridge = 0x02,
sdb_type_integration = 0x80,
sdb_type_empty = 0xFF,
};
/* Type 0: interconnect (first of the array)
*
* sdb_records is the length of the table including this first
* record, version is 1. The bus type is enumerated later.
*/
#define SDB_MAGIC 0x5344422d /* "SDB-" */
struct sdb_interconnect {
uint32_t sdb_magic; /* 0x00-0x03 */
uint16_t sdb_records; /* 0x04-0x05 */
uint8_t sdb_version; /* 0x06 */
uint8_t sdb_bus_type; /* 0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 1: device
*
* class is 0 for "custom device", other values are
* to be standardized; ABI version is for the driver,
* bus-specific bits are defined by each bus (see below)
*/
struct sdb_device {
uint16_t abi_class; /* 0x00-0x01 */
uint8_t abi_ver_major; /* 0x02 */
uint8_t abi_ver_minor; /* 0x03 */
uint32_t bus_specific; /* 0x04-0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 2: bridge
*
* child is the address of the nested SDB table
*/
struct sdb_bridge {
uint64_t sdb_child; /* 0x00-0x07 */
struct sdb_component sdb_component; /* 0x08-0x3f */
};
/* Type 0x80: integration
*
* all types with bit 7 set are meta-information, so
* software can ignore the types it doesn't know. Here we
* just provide product information for an aggregate device
*/
struct sdb_integration {
uint8_t reserved[24]; /* 0x00-0x17 */
struct sdb_product product; /* 0x08-0x3f */
};
/* Type 0xff: empty
*
* this allows keeping empty slots during development,
* so they can be filled later with miminal efforts and
* no misleading description is ever shipped -- hopefully.
* It can also be used to pad a table to a desired length.
*/
struct sdb_empty {
uint8_t reserved[63]; /* 0x00-0x3e */
uint8_t record_type; /* 0x3f */
};
/* The type of bus, for bus-specific flags */
enum sdb_bus_type {
sdb_wishbone = 0x00,
sdb_data = 0x01,
};
#define SDB_WB_WIDTH_MASK 0x0f
#define SDB_WB_ACCESS8 0x01
#define SDB_WB_ACCESS16 0x02
#define SDB_WB_ACCESS32 0x04
#define SDB_WB_ACCESS64 0x08
#define SDB_WB_LITTLE_ENDIAN 0x80
#define SDB_DATA_READ 0x04
#define SDB_DATA_WRITE 0x02
#define SDB_DATA_EXEC 0x01
#endif /* __SDB_H__ */
LINUX ?= /lib/modules/$(shell uname -r)/build
# get sdb.h from ../include/linux/
ccflags-y = -I$M/../include
obj-m = sdbfs.o
obj-m += sdb-fakedev.o
obj-m += sdb-iomem.o
sdbfs-y = sdbfs-core.o
sdbfs-y += sdbfs-file.o
sdbfs-y += sdbfs-inode.o
sdbfs-y += sdbfs-client.o
all: modules
install: modules_install
modules clean modules_install:
$(MAKE) -C $(LINUX) M=$(shell /bin/pwd) $@
/*
* 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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include "sdbfs.h"
/* We register up to 8 filesystems getting the names as module parameters */
static int fakedev_nimg;
static char *fakedev_fsimg[8];
module_param_array_named(fsimg, fakedev_fsimg, charp, &fakedev_nimg, 0444);
struct fakedev {
struct sdbfs_dev sd;
const struct firmware *fw;
};
static struct fakedev fakedev_devs[8];
static struct device fakedev_device;
static ssize_t fakedev_read(struct sdbfs_dev *sd, uint32_t begin, void *buf,
size_t count)
{
struct fakedev *fd;
int len;
printk("%s: %08x - %i\n", __func__, (int)begin, count);
fd = container_of(sd, struct fakedev, sd);
len = fd->fw->size;
if (begin > len)
return -EINVAL;
if (begin + count > len)
count = len - begin;
memcpy(buf, fd->fw->data + begin, count);
return count;
}
static struct sdbfs_dev_ops fakedev_ops = {
.owner = THIS_MODULE,
.erase = NULL,
.read = fakedev_read,
.write = NULL,
};
static int fakedev_init(void)
{
struct fakedev *d;
int i;
/* we need a device to request a firmware image */
dev_set_name(&fakedev_device, KBUILD_MODNAME);
device_initialize(&fakedev_device);
i = device_add(&fakedev_device);
if (i < 0) {
printk("%s: failed to init device (error %i)\n",
KBUILD_MODNAME, i);
return i;
}
for (i = 0; i < fakedev_nimg; i++) {
d = fakedev_devs + i;
if (request_firmware(&d->fw, fakedev_fsimg[i],
&fakedev_device) < 0) {
dev_err(&fakedev_device, "can't load %s\n",
fakedev_fsimg[i]);
continue;
}
d->sd.name = fakedev_fsimg[i];
d->sd.blocksize = 64; /* bah! */
d->sd.size = d->fw->size;
d->sd.ops = &fakedev_ops;
if (sdbfs_register_device(&d->sd) < 0) {
dev_err(&fakedev_device, "can't register %s\n",
fakedev_fsimg[i]);
release_firmware(d->fw);
d->fw = NULL;
}
}
return 0;
}
static void fakedev_exit(void)
{
struct fakedev *d;
int i;
for (i = 0; i < fakedev_nimg; i++) {
d = fakedev_devs + i;
if (!d->fw)
continue;
sdbfs_unregister_device(&d->sd);
release_firmware(d->fw);
}
device_del(&fakedev_device);
}
module_init(fakedev_init);
module_exit(fakedev_exit);
MODULE_LICENSE("GPL");
/*
* 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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/sdb.h>
#include "sdbfs.h"
struct sdbmem {
struct sdbfs_dev sd;
void __iomem *address;
size_t datalen;
int ready;
};
/* We register up to 8 filesystems getting the description at insmod */
static int sdbmem_narea;
static char *sdbmem_area[8];
module_param_array_named(area, sdbmem_area, charp, &sdbmem_narea, 0444);
/*
* area=<name>@<address>-<address>[=<entrypoint>]
* area=<name>@<address>+<lenght>[=<entrypoint>]
*/
static int sdbmem_parse(char *desc, struct sdbmem *d)
{
unsigned long addr, size;
char *at;
int i;
char c;
memset(d, 0, sizeof(*d));
at = strchr(desc, '@');
if (!at)
return -EINVAL;
i = sscanf(at,"@%lx+%lx=%lx%c", &addr, &size,
&d->sd.entrypoint, &c);
if (i == 1) {
i = sscanf(at,"@%lx-%lx=%lx%c", &addr, &size,
&d->sd.entrypoint, &c);
size -= addr;
}
if (i < 2 || i > 3) {
pr_err("%s: wrong argument \"%s\"\n", KBUILD_MODNAME,
desc);
pr_err("Use \"<name>@<addr>[-+]<addr>[=<entrypoint>\"\n");
return -EINVAL;
}
/* So, the name is the first one and there is the '@' sign */
*at = '\0';
d->sd.name = desc;
d->address = ioremap(addr, size);
if (!d->address)
return -ENOMEM;
d->sd.size = d->datalen = size;
return 0;
}
static struct sdbmem sdbmem_devs[8];
static ssize_t sdbmem_read(struct sdbfs_dev *sd, uint32_t begin, void *buf,
size_t count)
{
struct sdbmem *fd;
size_t len;
printk("%s: %08x - %i\n", __func__, (int)begin, count);
fd = container_of(sd, struct sdbmem, sd);
len = fd->datalen;
if (begin > len)
return -EINVAL;
if (begin + count > len)
count = len - begin;
memcpy_fromio(buf, fd->address + begin, count);
return count;
}
static struct sdbfs_dev_ops sdbmem_ops = {
.owner = THIS_MODULE,
.erase = NULL,
.read = sdbmem_read,
.write = NULL,
};
/* FIXME: export the register and unregister functions for external users */
static int sdbmem_init(void)
{
struct sdbmem *d;
int i, done = 0;
uint32_t magic;
for (i = 0; i < sdbmem_narea; i++) {
d = sdbmem_devs + i;
if (sdbmem_parse(sdbmem_area[i], d) < 0)
continue;
/* Check the magic number, so we fail ASAP */
magic = readl(d->address + d->sd.entrypoint);
if (magic != SDB_MAGIC && magic != ntohl(SDB_MAGIC)) {
iounmap(d->address);
pr_err("%s: wrong magic 0x%08x at 0x%lx\n", __func__,
magic, d->sd.entrypoint);
continue;
}
d->sd.blocksize = 4; /* bah! */
d->sd.ops = &sdbmem_ops;
if (sdbfs_register_device(&d->sd) < 0) {
pr_err("%s: can't register area %s\n", KBUILD_MODNAME,
d->sd.name);
continue;
}
done++;
d->ready = 1;
}
if (done)
return 0;
return -ENODEV;
}
static void sdbmem_exit(void)
{
struct sdbmem *d;
int i;
for (i = 0; i < sdbmem_narea; i++) {
d = sdbmem_devs + i;
if (!d->ready)
continue;
sdbfs_unregister_device(&d->sd);
iounmap(d->address);
}
}
module_init(sdbmem_init);
module_exit(sdbmem_exit);
MODULE_LICENSE("GPL");
/*
* 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 <linux/module.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/sdb.h> /* in ../include, by now */
#include "sdbfs.h"
#include "sdbfs-int.h"
static LIST_HEAD(sdbfs_devlist);
struct sdbfs_dev *sdbfs_get_by_name(char *name)
{
struct sdbfs_dev *sd;
list_for_each_entry(sd, &sdbfs_devlist, list)
if (!strcmp(sd->name, name))
goto found;
return ERR_PTR(-ENOENT);
found:
if (try_module_get(sd->ops->owner)) {
printk("%s: %p\n", __func__, sd);
return sd;
}
return ERR_PTR(-ENOENT);
}
void sdbfs_put(struct sdbfs_dev *sd)
{
printk("%s: %p\n", __func__, sd);
module_put(sd->ops->owner);
}
/* Exported functions */
int sdbfs_register_device(struct sdbfs_dev *sd)
{
struct sdbfs_dev *osd;
list_for_each_entry(osd, &sdbfs_devlist, list)
if (!strcmp(osd->name, sd->name))
return -EBUSY;
list_add(&sd->list, &sdbfs_devlist);
list_for_each_entry(osd, &sdbfs_devlist, list)
printk("list entry %p\n", osd);
return 0;
}
EXPORT_SYMBOL(sdbfs_register_device);
void sdbfs_unregister_device(struct sdbfs_dev *sd)
{
struct sdbfs_dev *osd;
list_for_each_entry(osd, &sdbfs_devlist, list)
printk("list entry %p\n", osd);
printk("removing %p\n", sd);
list_for_each_entry(osd, &sdbfs_devlist, list)
if (osd == sd)
break;
if (osd == sd)
list_del(&sd->list);
}
EXPORT_SYMBOL(sdbfs_unregister_device);
/*
* 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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/sdb.h> /* in ../include, by now */
#include "sdbfs-int.h"
static const struct super_operations sdbfs_super_ops = {
.alloc_inode = sdbfs_alloc_inode,
.destroy_inode = sdbfs_destroy_inode,
};
static int sdbfs_fill_super(struct super_block *sb, void *data, int silent)
{
struct inode *inode;
struct dentry *root;
struct sdbfs_dev *sd;
uint32_t magic;
printk("%s\n", __func__);
/* HACK: this data is really a name */
sd = sdbfs_get_by_name(data);
if (IS_ERR(sd))
return PTR_ERR(sd);
sb->s_fs_info = sd;
/* Check magic number first */
sd->ops->read(sd, sd->entrypoint, &magic, 4);
if (magic == ntohl(SDB_MAGIC)) {
/* all right: we are big endian or byte-level connected */
} else if (magic == SDB_MAGIC) {
/* looks like we are little-endian on a 32-bit-only bus */
sd->flags |= SDBFS_F_FIXENDIAN;
} else {
printk("%s: wrong magic at 0x%lx (%08x is not %08x)\n",
__func__, sd->entrypoint, magic, SDB_MAGIC);
return -EINVAL;
}
/* All of our data is organized as 64-byte blocks */
sb->s_blocksize = 64;
sb->s_blocksize_bits = 6;
sb->s_magic = SDB_MAGIC;
sb->s_op = &sdbfs_super_ops;
/* The root inode is 1. It is a fake bridge and has no parent. */
inode = sdbfs_iget(NULL, sb, SDBFS_ROOT);
if (IS_ERR(inode)) {
sdbfs_put(sd);
return PTR_ERR(inode);
}
/*
* Instantiate and link root dentry. d_make_root only exists
* after 3.2, but d_alloc_root was killed soon after 3.3
*/
root = d_make_root(inode);
if (!root) {
sdbfs_put(sd);
/* FIXME: release inode? */
return -ENOMEM;
}
root->d_fsdata = NULL; /* FIXME: d_fsdata */
sb->s_root = root;
return 0;
}
static struct dentry *sdbfs_mount(struct file_system_type *type, int flags,
const char *name, void *data)
{
struct dentry *ret;
char *fakedata = (char *)name;
/* HACK: use "name" as data, to use the mount_single helper */
ret = mount_single(type, flags, fakedata, sdbfs_fill_super);
printk("%s: %p\n", __func__, ret);
return ret;
}
static void sdbfs_kill_sb(struct super_block *sb)
{
struct sdbfs_dev *sd = sb->s_fs_info;
printk("%s\n", __func__);
kill_anon_super(sb);
if (sd)
sdbfs_put(sd);
}
static struct file_system_type sdbfs_fs_type = {
.owner = THIS_MODULE,
.name = "sdbfs",
.mount = sdbfs_mount, /* 2.6.37 and later only */
.kill_sb = sdbfs_kill_sb,
};
struct kmem_cache *sdbfs_inode_cache;
static int __init sdbfs_init(void)
{
sdbfs_inode_cache = KMEM_CACHE(sdbfs_inode, 0);
if (!sdbfs_inode_cache)
return -ENOMEM;
return register_filesystem(&sdbfs_fs_type);
return 0;
}
static void __exit sdbfs_exit(void)
{
unregister_filesystem(&sdbfs_fs_type);
kmem_cache_destroy(sdbfs_inode_cache);
}
module_init(sdbfs_init);
module_exit(sdbfs_exit);
MODULE_LICENSE("GPL");
/*
* 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 <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include "sdbfs-int.h"
static ssize_t sdbfs_read(struct file *f, char __user *buf, size_t count,
loff_t *offp)
{
struct inode *ino = f->f_dentry->d_inode;
struct super_block *sb = ino->i_sb;
struct sdbfs_dev *sd = sb->s_fs_info;
struct sdbfs_inode *inode;
char kbuf[16];
unsigned long start, size;
ssize_t i, done;
inode = container_of(ino, struct sdbfs_inode, ino);
start = be64_to_cpu(inode->info.s_d.sdb_component.addr_first);
size = be64_to_cpu(inode->info.s_d.sdb_component.addr_last) + 1 - start;
if (*offp > size)
return 0;
if (*offp + count > size)
count = size - *offp;
done = 0;
while (done < count) {
/* Horribly inefficient, just copy a few bytes at a time */
int n = sizeof(kbuf) > count ? count : sizeof(kbuf);
/* FIXME: error checking */
i = sd->ops->read(sd, start + *offp, kbuf, n);
if (i < 0) {
if (done)
return done;
return i;
}
if (copy_to_user(buf, kbuf, i))
return -EFAULT;
buf += i;
done += i;
if (i != n) {
/* Partial read: done for this time */
break;
}
}
*offp += done;
return done;
}
const struct file_operations sdbfs_fops = {
.read = sdbfs_read,
};
/*
* 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 <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/sdb.h> /* in ../include, by now */
#include "sdbfs-int.h"
static void sdbfs_fix_endian(struct sdbfs_dev *sd, void *ptr, int len)
{
uint32_t *p = ptr;
int i;
if (!(sd->flags & SDBFS_F_FIXENDIAN))
return;
if (len & 3)
return; /* Hmmm... */
for (i = 0; i < len / 4; i++)
p[i] = htonl(p[i]);
}
/* This is called by readdir and by lookup, when needed */
static int sdbfs_read_whole_dir(struct sdbfs_inode *inode)
{
struct sdbfs_info *info;
struct super_block *sb = inode->ino.i_sb;
struct sdbfs_dev *sd = sb->s_fs_info;
unsigned long offset;
int i, j, n;
if (inode->nfiles)
return 0;
printk("%s\n", __func__);
/* Get the interconnect and see how many */
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
n = sd->ops->read(sd, inode->base_sdb, &info->s_i, SDB_SIZE);
if (n != SDB_SIZE) {
kfree(info);
return -EIO;
}
sdbfs_fix_endian(sd, &info->s_i, SDB_SIZE);
if (info->s_i.sdb_magic != htonl(SDB_MAGIC)) {
pr_err("%s: wrong magic (%08x) at offset 0x%lx\n", __func__,
info->s_i.sdb_magic, inode->base_sdb);
kfree(info);
return -EINVAL;
}
inode->nfiles = be16_to_cpu(info->s_i.sdb_records);
kfree(info);
printk("nfiles %i\n", inode->nfiles);
info = kmalloc(sizeof(*info) * inode->nfiles, GFP_KERNEL);
if (!info) {
inode->nfiles = 0;
return -ENOMEM;
}
offset = inode->base_sdb;
printk("reading at 0x%lx\n", offset);
inode->files = info;
for (i = 0; i < inode->nfiles; i++) {
info = inode->files + i;
n = sd->ops->read(sd, offset, &info->s_d, SDB_SIZE);
if (n != SDB_SIZE) {
/* FIXME: iput? */
kfree(inode->files);
inode->nfiles = 0;
return -EIO;
}
sdbfs_fix_endian(sd, &info->s_d, SDB_SIZE);
strncpy(info->name,
info->s_d.sdb_component.product.name, 19);
for (j = 19; j; j--) {
info->name[j] = '\0';
if (info->name[j-1] != ' ')
break;
}
info->namelen = j;
offset += SDB_SIZE;
}
return 0;
}
static int sdbfs_readdir(struct file * filp,
void * dirent, filldir_t filldir)
{
struct inode *ino = filp->f_dentry->d_inode;
struct sdbfs_inode *inode;
struct sdbfs_info *info;
unsigned long offset;
int i, type, done = 0;
printk("%s\n", __func__);
/* dot and dotdot are special */
if (filp->f_pos == 0) {
if (filldir(dirent, ".", 1, 0, ino->i_ino, DT_DIR) < 0)
return done;
done++;
filp->f_pos++;
}
if (filp->f_pos == 1) {
if (filldir(dirent, "..", 2, 1,
parent_ino(filp->f_dentry), DT_DIR) < 0)
return done;
done++;
filp->f_pos++;
}
/* Then our stuff */
inode = container_of(ino, struct sdbfs_inode, ino);
sdbfs_read_whole_dir(inode);
offset = inode->base_sdb;
for (i = filp->f_pos - 2; i < inode->nfiles; i++) {
info = inode->files + i;
if (info->s_e.record_type == sdb_type_bridge)
type = DT_DIR;
else
type = DT_REG;
if (filldir(dirent, info->name, info->namelen,
SDBFS_INO(offset), i + 2 /* ? */, type) < 0)
return done;
filp->f_pos++;
done++;
}
return done;
}
static const struct file_operations sdbfs_dir_fops = {
.read = generic_read_dir,
.readdir = sdbfs_readdir,
.llseek = default_llseek,
};
static struct dentry *sdbfs_lookup(struct inode *dir,
struct dentry *dentry, struct nameidata *nd)
{
struct inode *ino = NULL;
struct sdbfs_inode *inode = container_of(dir, struct sdbfs_inode, ino);
struct sdbfs_info *info;
unsigned long offset = inode->base_sdb;
int i, n, len;
printk("%s\n", __func__);
sdbfs_read_whole_dir(inode);
n = inode->nfiles;
len = dentry->d_name.len;
for (i = 0; i < n; i++) {
info = inode->files + i;
if (info->namelen != len)
continue;
if (!strncmp(info->name, dentry->d_name.name, len))
break;
}
if (i != n) {
offset = offset + SDB_SIZE * i;
ino = sdbfs_iget(inode, dir->i_sb, SDBFS_INO(offset));
}
d_add(dentry, ino);
return 0;
}
static const struct inode_operations sdbfs_dir_iops = {
.lookup = sdbfs_lookup,
};
struct inode *sdbfs_alloc_inode(struct super_block *sb)
{
struct sdbfs_inode *inode;
printk("%s\n", __func__);
inode = kmem_cache_alloc(sdbfs_inode_cache, GFP_KERNEL);
if (!inode)
return NULL;
inode_init_once(&inode->ino);
printk("%s: return %p\n", __func__, &inode->ino);
return &inode->ino;
}
void sdbfs_destroy_inode(struct inode *ino)
{
struct sdbfs_inode *inode;
inode = container_of(ino, struct sdbfs_inode, ino);
kfree(inode->files);
kmem_cache_free(sdbfs_inode_cache, inode);
}
static struct inode *sdbfs_iget_root(struct super_block *sb,
struct inode *ino)
{
struct sdbfs_dev *sd = sb->s_fs_info;
struct sdbfs_inode *inode = container_of(ino, struct sdbfs_inode, ino);
struct sdb_bridge *b = &inode->info.s_b;
/* The root directory is a fake "bridge" structure */
memset(b, 0, sizeof(*b));
b->sdb_child = cpu_to_be64(sd->entrypoint);
b->sdb_component.addr_first = 0;
b->sdb_component.addr_last = cpu_to_be64(sd->size);
b->sdb_component.product.record_type = sdb_type_bridge;
/* So, this is a directory, and it links to the first interconnect */
inode->base_data = 0;
inode->base_sdb = sd->entrypoint;
ino->i_size = sd->size;
ino->i_mode = S_IFDIR | 0555;
ino->i_op = &sdbfs_dir_iops;
ino->i_fop = &sdbfs_dir_fops;
return ino;
}
struct inode *sdbfs_iget(struct sdbfs_inode *parent,
struct super_block *sb, unsigned long inum)
{
struct inode *ino;
struct sdbfs_dev *sd = sb->s_fs_info;
struct sdbfs_inode *inode;
uint32_t offset;
unsigned long size, base_data; /* target offset */
int n;
int type;
printk("%s: inum 0x%lx\n", __func__, inum);
ino = iget_locked(sb, inum);
if (!ino)
return ERR_PTR(-ENOMEM);
if (!(ino->i_state & I_NEW))
return ino;
/* general setup; no link concept: the structure is immutable */
set_nlink(ino, 1);
ino->i_mtime.tv_sec = ino->i_atime.tv_sec = ino->i_ctime.tv_sec = 0;
ino->i_mtime.tv_nsec = ino->i_atime.tv_nsec = ino->i_ctime.tv_nsec = 0;
if (unlikely(!parent)) { /* inum == SDBFS_ROOT */
sdbfs_iget_root(sb, ino);
unlock_new_inode(ino);
return ino;
}
inode = container_of(ino, struct sdbfs_inode, ino);
offset = SDBFS_OFFSET(inum);
n = sd->ops->read(sd, offset, &inode->info.s_d, SDB_SIZE);
if (n != SDB_SIZE)
return ERR_PTR(-EIO);
sdbfs_fix_endian(sd, &inode->info.s_d, SDB_SIZE);
base_data = be64_to_cpu(inode->info.s_d.sdb_component.addr_first);
size = be64_to_cpu(inode->info.s_d.sdb_component.addr_last)
- base_data + 1;
type = inode->info.s_e.record_type;
switch (type) {
case sdb_type_interconnect:
case sdb_type_device:
/* You can access internal registers/data */
ino->i_fop = &sdbfs_fops; /* FIXME: Which bus type? */
ino->i_mode = S_IFREG | 0444;
ino->i_size = size;
inode->base_data = parent->base_data + base_data;
break;
case sdb_type_bridge:
/* A bridge is a subdirectory */
ino->i_mode = S_IFDIR | 0555;
ino->i_op = &sdbfs_dir_iops;
ino->i_fop = &sdbfs_dir_fops;
inode->base_data = parent->base_data + base_data;
inode->base_sdb = parent->base_data
+ be64_to_cpu(inode->info.s_b.sdb_child);
break;
default:
if (type & 0x80) /* informative only */
pr_info("%s: ignoring unknown record 0x%02x\n",
__func__, type);
else
pr_err("%s: unsupported record 0x%02x\n",
__func__, type);
break;
}
unlock_new_inode(ino);
return ino;
}
/*
* 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.
*/
#ifndef __SDBFS_INT_H__
#define __SDBFS_INT_H__
#include <linux/fs.h>
#include <linux/sdb.h>
#include "sdbfs.h"
/* This is our mapping of inode numbers */
#define SDBFS_ROOT 1
#define SDBFS_INO(offset) ((offset) + 2)
#define SDBFS_OFFSET(ino) ((ino) & ~15)
#define SDB_SIZE (sizeof(struct sdb_device))
struct sdbfs_info {
/* unnamed union to save typing */
union {
struct sdb_device s_d;
struct sdb_interconnect s_i;
struct sdb_bridge s_b;
struct sdb_empty s_e;
};
char name[20]; /* 19 + terminator */
int namelen;
};
struct sdbfs_inode {
struct sdbfs_info info;
int nfiles;
struct sdbfs_info *files; /* for directories */
struct inode ino;
/* below, the former is the base for relative addresses */
unsigned long base_data;
unsigned long base_sdb;
};
/* This is needed to convert endianness. Hoping it is not defined elsewhere */
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;
}
static inline uint64_t ntohll(uint64_t ll)
{
return htonll(ll);
}
/* Material in sdbfs-file.c */
extern const struct file_operations sdbfs_fops;
/* Material in sdbfs-inode.c */
struct inode *sdbfs_alloc_inode(struct super_block *sb);
void sdbfs_destroy_inode(struct inode *ino);
struct inode *sdbfs_iget(struct sdbfs_inode *parent,
struct super_block *sb, unsigned long inum);
extern struct kmem_cache *sdbfs_inode_cache;
#endif /* __SDBFS_INT_H__ */
/*
* 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.
*/
#ifndef __SDBFS_H__
#define __SDBFS_H__
struct sdbfs_dev;
/*
* Client modules register sdbfs-capable devices, and several of them
* may use the same hardware abstraction, so separate it. We use 32-bit
* addresses, being aware of potential issues, but our target is small
* devices accessed by a soft-core. If the need arises, we'll add erase64
* and similar methods.
*/
struct sdbfs_dev_ops {
struct module *owner;
int (*erase)(struct sdbfs_dev *sd, uint32_t begin, uint32_t end);
ssize_t (*read)(struct sdbfs_dev *sd, uint32_t begin, void *buf,
size_t count);
ssize_t (*write)(struct sdbfs_dev *sd, uint32_t begin, void *buf,
size_t count);
};
struct sdbfs_dev {
char *name;
unsigned long flags;
int blocksize;
unsigned long entrypoint;
struct sdbfs_dev_ops *ops;
struct list_head list;
unsigned long size;
/* Following is private to the FS code */
unsigned long ino_base;
};
/* flags */
#define SDBFS_F_FIXENDIAN 0x0001
/* Internal inter-file calls */
struct sdbfs_dev *sdbfs_get_by_name(char *name);
void sdbfs_put(struct sdbfs_dev *sd);
/* Exported to other modules */
int sdbfs_register_device(struct sdbfs_dev *sd);
void sdbfs_unregister_device(struct sdbfs_dev *sd);
#endif /* __SDBFS_H__ */
LINUX ?= /lib/modules/$(shell uname -r)/build
# If we compile for the kernel, we need to include real kernel headers.
# The thing is enough a mess that I moved it to a different file
include Makefile.arch
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
# calculate endianness at compile time
ENDIAN := $(shell ./check-endian $(CC))
CFLAGS = -Wall -ggdb -O2
CFLAGS += -I../include/linux # for <sdb.h>
CFLAGS += -ffunction-sections -fdata-sections
CFLAGS += -Wno-pointer-sign
CFLAGS += $(ENDIAN) $(LINUXINCLUDE)
LIB = libsdbfs.a
OBJS = glue.o access.o
all: $(LIB)
$(OBJS): $(wildcard *.h)
$(LIB): $(OBJS)
$(AR) r $@ $(OBJS)
clean:
rm -f $(OBJS) $(LIB) *~ core
# add the other unused targets, so the rule in ../Makefile works
modules install modules_install:
srctree = $(LINUX)
#
# This set of contortions comes from the kernel Makefile. We need this
# in order to properly compile libsdbfs for the kernel without being
# in a kernel build environment (for example, to check for compile errors).
#
ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
-e s/arm.*/arm/ -e s/sa110/arm/ \
-e s/s390x/s390/ -e s/parisc64/parisc/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ )
SRCARCH := $(ARCH)
# Additional ARCH settings for x86
ifeq ($(ARCH),i386)
SRCARCH := x86
endif
ifeq ($(ARCH),x86_64)
SRCARCH := x86
endif
# Additional ARCH settings for sparc
ifeq ($(ARCH),sparc32)
SRCARCH := sparc
endif
ifeq ($(ARCH),sparc64)
SRCARCH := sparc
endif
# Additional ARCH settings for sh
ifeq ($(ARCH),sh64)
SRCARCH := sh
endif
# Additional ARCH settings for tile
ifeq ($(ARCH),tilepro)
SRCARCH := tile
endif
ifeq ($(ARCH),tilegx)
SRCARCH := tile
endif
# Where to locate arch specific headers
hdr-arch := $(SRCARCH)
ifeq ($(ARCH),m68knommu)
hdr-arch := m68k
endif
# Use LINUXINCLUDE when you must reference the include/ directory.
# Needed to be compatible with the O= option
LINUXINCLUDE := -I$(srctree)/arch/$(hdr-arch)/include \
-Iarch/$(hdr-arch)/include/generated \
-I$(srctree)/include
/*
* 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.
*/
/* To avoid many #ifdef and associated mess, all headers are included there */
#include "libsdbfs.h"
int sdbfs_fstat(struct sdbfs *fs, struct sdb_device *record_return)
{
if (!fs->currentp)
return -ENOENT;
memcpy(record_return, fs->currentp, sizeof(*record_return));
return 0;
}
int sdbfs_fread(struct sdbfs *fs, int offset, char *buf, int count)
{
if (!fs->currentp)
return -ENOENT;
if (offset < 0)
offset = fs->read_offset;
if (offset + count > fs->f_len)
count = fs->f_len - offset;
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;
}
int sdbfs_fwrite(struct sdbfs *fs, int offset, char *buf, int count)
{
return -ENOSYS;
}
#!/bin/bash
# Check endianness at compile time, so we can pass the -D to CFLAGS
CC=$1
if [ "x$CC" == "x" ]; then
echo "$0: pass the compiler path (\$CC) as argument" >& 2
exit 1
fi
# Check endianness, by making an object file
TMPC=$(mktemp /tmp/endian-c-XXXXXX)
TMPO=$(mktemp /tmp/endian-o-XXXXXX)
echo "int i = 0xbbee;" > $TMPC
$CC -x c -c $TMPC -o $TMPO
OBJCOPY=$(echo $CC | sed 's/gcc$/objcopy/')
if $OBJCOPY -O binary $TMPO /dev/stdout | od -t x1 -An | \
grep -q 'bb ee'; then
echo " -DSDBFS_BIG_ENDIAN"
else
echo " -DSDBFS_LITTLE_ENDIAN"
fi
rm -f $TMPC $TMPO
/*
* 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.
*/
/* To avoid many #ifdef and associated mess, all headers are included there */
#include "libsdbfs.h"
static struct sdbfs *sdbfs_list;
/* All fields unused by the caller are expected to be zeroed */
int sdbfs_dev_create(struct sdbfs *fs, int verbose)
{
unsigned int magic;
/* First, check we have the magic */
if (fs->data)
magic = *(unsigned int *)(fs->data + fs->entrypoint);
else
fs->read(fs, fs->entrypoint, &magic, sizeof(magic));
if (htonl(magic) != SDB_MAGIC)
return -ENOTDIR;
if (verbose)
fs->flags |= SDBFS_F_VERBOSE;
fs->next = sdbfs_list;
sdbfs_list = fs;
return 0;
}
int sdbfs_dev_destroy(struct sdbfs *fs)
{
struct sdbfs **p;
for (p = &sdbfs_list; *p && *p != fs; p = &(*p)->next)
;
if (!*p)
return -ENOENT;
*p = fs->next;
return 0;
}
struct sdbfs *sdbfs_dev_find(const char *name)
{
struct sdbfs *l;
for (l = sdbfs_list; l && strcmp(l->name, name); l = l->next)
;
if (!l)
return NULL;
return l;
}
/*
* To open by name or by ID we need to scan the tree. The scan
* function is also exported in order for "sdb-ls" to use it
*/
static struct sdb_device *sdbfs_readentry(struct sdbfs *fs,
unsigned long offset)
{
/*
* This function reads an entry from a known good offset. It
* returns the pointer to the entry, which may be stored in
* the fs structure itself. Only touches fs->current_record
*/
if (fs->data)
return (struct sdb_device *)(fs->data + offset);
fs->read(fs, offset, &fs->current_record, sizeof(fs->current_record));
return &fs->current_record;
}
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
*/
struct sdb_device *ret;
struct sdb_interconnect *i;
if (newscan) {
fs->f_offset = fs->entrypoint;
} else {
fs->f_offset += sizeof(struct sdb_device);
if (!fs->nleft)
return NULL;
}
ret = sdbfs_readentry(fs, fs->f_offset);
if (newscan) {
i = (typeof(i))ret;
fs->nleft = ntohs(i->sdb_records) - 1;
} else {
fs->nleft--;
}
return ret;
}
static void __open(struct sdbfs *fs)
{
fs->f_offset = htonll(fs->currentp->sdb_component.addr_first);
fs->f_len = htonll(fs->currentp->sdb_component.addr_last)
+ 1 - fs->f_offset;
fs->read_offset = 0;
}
int sdbfs_open_name(struct sdbfs *fs, const char *name)
{
struct sdb_device *d;
int len = strlen(name);
if (len > 19)
return -ENOENT;
sdbfs_scan(fs, 1); /* new scan: get the interconnect and igore it */
while ( (d = sdbfs_scan(fs, 0)) != NULL) {
if (strncmp(name, d->sdb_component.product.name, len))
continue;
if (len < 19 && d->sdb_component.product.name[len] != ' ')
continue;
fs->currentp = d;
__open(fs);
return 0;
}
return -ENOENT;
}
int sdbfs_open_id(struct sdbfs *fs, uint64_t vid, uint32_t did)
{
struct sdb_device *d;
sdbfs_scan(fs, 1); /* new scan: get the interconnect and igore it */
while ( (d = sdbfs_scan(fs, 0)) != NULL) {
if (vid != d->sdb_component.product.vendor_id)
continue;
if (did != d->sdb_component.product.device_id)
continue;
fs->currentp = d;
__open(fs);
return 0;
}
return -ENOENT;
}
int sdbfs_close(struct sdbfs *fs)
{
fs->currentp = NULL;
return 0;
}
/* Though freestanding, some minimal headers are expected to exist */
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#define SDB_KERNEL 0
#define SDB_USER 0
#define SDB_FREESTAND 1
#ifdef SDBFS_BIG_ENDIAN
# define ntohs(x) (x)
# define htons(x) (x)
# define ntohl(x) (x)
# define htonl(x) (x)
#else
# error "No support, yet, for little-endian freestanding library"
#endif
#include <linux/types.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <asm/byteorder.h>
/*
* The default installed /usr/include/linux stuff misses the __KERNEL__ parts.
* For libsdbfs it means we won't get uint32_t and similar types.
*
* So, check if we got the information we need before strange errors happen.
* The DECLARE_BITMAP macro is in <linux/types.h> since the epoch, but it
* is not installed in /usr/include/linux/types.h, so use it to check.
*/
#ifndef DECLARE_BITMAP
# error "Please point LINUX to a source tree if you define __KERNEL__"
#endif
#define SDB_KERNEL 1
#define SDB_USER 0
#define SDB_FREESTAND 0
#define sdb_print(format, ...) printk(format, __VA_ARGS__)
#ifndef __LIBSDBFS_USER_H__
#define __LIBSDBFS_USER_H__
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h> /* htonl */
#define SDB_KERNEL 0
#define SDB_USER 1
#define SDB_FREESTAND 0
#define sdb_print(format, ...) fprintf(stderr, format, __VA_ARGS__)
#endif /* __LIBSDBFS_USER_H__ */
#ifndef __LIBSDBFS_H__
#define __LIBSDBFS_H__
/* The library can work in three different environments */
#ifdef __KERNEL__
# include "libsdbfs-kernel.h"
#elif defined(__unix__)
# include "libsdbfs-user.h"
#else
# include "libsdbfs-freestanding.h"
#endif
#include <sdb.h> /* Please point your "-I" to some sensible place */
/*
* Data structures: please not that the library intself doesn't use
* malloc, so it's the caller who must deal withallocation/removal.
* For this reason we can have no opaque structures, but some fields
* are private
*/
struct sdbfs {
/* Some fields are informative */
char *name; /* may be null */
void *drvdata; /* driver may need some detail.. */
int blocksize;
unsigned long entrypoint;
/* The "driver" must offer some methods */
void *data; /* Use this if directly mapped */
unsigned long datalen; /* Length of the above array */
int (*read)(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);
/* The following fields are library-private */
struct sdb_device current_record;
struct sdb_device *currentp;
int nleft;
unsigned long f_offset;
unsigned long f_len;
unsigned long read_offset;
unsigned long flags;
struct sdbfs *next;
};
#define SDBFS_F_VERBOSE 0x0001
/* Defined in glue.c */
int sdbfs_dev_create(struct sdbfs *fs, int verbose);
int sdbfs_dev_destroy(struct sdbfs *fs);
struct sdbfs *sdbfs_dev_find(const char *name);
int sdbfs_open_name(struct sdbfs *fs, const char *name);
int sdbfs_open_id(struct sdbfs *fs, uint64_t vid, uint32_t did);
int sdbfs_close(struct sdbfs *fs);
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);
/* This is needed to convert endianness. Hoping it is not defined elsewhere */
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;
}
static inline uint64_t ntohll(uint64_t ll)
{
return htonll(ll);
}
#endif /* __LIBSDBFS_H__ */
# 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../lib -I../include -I../include/linux
LDFLAGS = -L../lib -lsdbfs
PROG = gensdbfs sdb-read
all: $(PROG)
%: %.c
$(CC) $(CFLAGS) -o $@ $*.c $(LDFLAGS)
$(PROG): ../lib/libsdbfs.a
clean:
rm -f $(PROG) *.o *~ core
# add the other unused targets, so the rule in ../Makefile works
modules install modules_install:
This diff is collapsed.
#ifndef __GENSDBFS_H__
#define __GENSDBFS_H__
#include <stdint.h>
#define CFG_NAME "--SDB-CONFIG--"
/* 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 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;
};
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;
}
#endif /* __GENSDBFS_H__ */
/*
* 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 <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;
/*
* This read method is not really needed, but it's there
* to exercise the library procedures
*/
static int do_read(struct sdbfs *fs, int offset, void *buf, int count)
{
if (opt_verbose)
fprintf(stderr, "%s @ 0x%08x - size 0x%x (%i)\n", __func__,
offset, count, count);
memcpy(buf, fs->drvdata + offset, count);
return count;
}
/* Boring ascii representation of a device */
static void list_device(struct sdb_device *d)
{
struct sdb_product *p;
struct sdb_component *c;
c = &d->sdb_component;
p = &c->product;
static int warned;
if (!opt_long) {
printf("%.19s\n", p->name);
return;
}
if (!warned) {
fprintf(stderr, "%s: listing format is to be defined\n",
prgname);
warned = 1;
}
printf("%016llx:%08x @ %08llx-%08llx %.19s\n",
ntohll(p->vendor_id), ntohl(p->device_id),
ntohll(c->addr_first), ntohll(c->addr_last),
p->name);
}
/* The following three function perform the real work, main() is just glue */
static void do_list(struct sdbfs *fs)
{
struct sdb_device *d;
int new = 1;
while ( (d = sdbfs_scan(fs, new)) != NULL) {
list_device(d);
new = 0;
}
}
static void 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);
}
static void 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, vendor,
dev, strerror(-i));
exit(1);
}
while ( (i = sdbfs_fread(fs, -1, buf, sizeof(buf))) > 0)
fwrite(buf, 1, i, stdout);
sdbfs_close(fs);
}
/* 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;
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:")) != -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;
}
}
if (optind < argc - 2 || optind > argc - 1) {
fprintf(stderr, "%s: Use: \"%s [-l|-v] <image-file> [<file>]\n",
prgname, prgname);
exit(1);
}
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, stbuf.st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0);
if (mapaddr == MAP_FAILED) {
fprintf(stderr, "%s: mmap(%s): %s\n", prgname, fsname,
strerror(errno));
exit(1);
}
/* So, describe the filesystem instance and give it to the library */
memset(fs, 0, sizeof(*fs));
fs->name = fsname; /* not mandatory */
fs->blocksize = 256; /* only used for writing, actually */
fs->entrypoint = opt_entry;
if (opt_read) {
fs->drvdata = mapaddr;
fs->read = do_read;
} else {
fs->data = mapaddr;
}
err = sdbfs_dev_create(fs, opt_verbose);
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)
do_list(fs);
else if (sscanf(filearg, "%llx:%lx", &int64, &int32) != 2)
do_cat_name(fs, filearg);
else
do_cat_id(fs, int64, int32);
sdbfs_dev_destroy(fs);
return 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