Commit f88c4330 authored by Lucas Russo's avatar Lucas Russo

foreign/libsdb/*: add new library for SDB parsing

This library was taken as is from
http://www.ohwr.org/projects/wrpc-sw repository,
located in sdb-lib, written by Alessandro Rubini and
Grzegorz Daniluk
parent 28881459
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 # 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,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.
*/
/* 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, 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
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, 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(fs->data + fs->f_offset + offset, buf, count);
else
ret = fs->write(fs, fs->f_offset + offset, buf, count);
if (ret > 0)
fs->read_offset = offset + ret;
return ret;
}
int sdbfs_ferase(struct sdbfs *fs, int offset, 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)
memset(fs->data + fs->f_offset + offset, 0xFF, count);
else
ret = fs->erase(fs, fs->f_offset + offset, count);
if (ret > 0)
fs->read_offset = offset + ret;
return ret;
}
#!/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,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.
*/
/* 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)
{
unsigned int magic;
/* First, check we have the magic */
if (fs->data || (fs->flags & SDBFS_F_ZEROBASED))
magic = *(unsigned int *)(fs->data + fs->entrypoint);
else
fs->read(fs, fs->entrypoint, &magic, sizeof(magic));
if (magic == SDB_MAGIC) {
/* Uh! If we are little-endian, we must convert */
if (ntohl(1) != 1)
fs->flags |= SDBFS_F_CONVERT32;
} else if (htonl(magic) == SDB_MAGIC) {
/* ok, don't convert */
} else {
return -ENOTDIR;
}
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 || (fs->flags & SDBFS_F_ZEROBASED)) {
if (!(fs->flags & SDBFS_F_CONVERT32))
return (struct sdb_device *)(fs->data + offset);
/* copy to local storage for conversion */
memcpy(&fs->current_record, fs->data + offset,
sizeof(fs->current_record));
} else {
if (!fs->read)
return NULL;
fs->read(fs, offset, &fs->current_record,
sizeof(fs->current_record));
}
if (fs->flags & SDBFS_F_CONVERT32) {
uint32_t *p = (void *)&fs->current_record;
int i;
for (i = 0; i < sizeof(fs->current_record) / sizeof(*p); i++)
p[i] = ntohl(p[i]);
}
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 the first one.
* Subdirectories (bridges) are returned before their contents.
* It only uses internal fields.
*/
struct sdb_device *dev;
struct sdb_bridge *bridge;
int depth, type, newdir = 0; /* check there's the magic */
if (newscan) {
fs->base[0] = 0;
fs->this[0] = fs->entrypoint;
depth = fs->depth = 0;
newdir = 1;
goto scan;
}
/* 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)
goto out;
/* Otherwise the directory is not there: no intercon */
if (!depth)
return NULL; /* no entries at all */
depth--;
}
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]--;
out:
fs->f_offset = fs->base[fs->depth]
+ htonll(fs->currentp->sdb_component.addr_first);
return dev;
}
static void __open(struct sdbfs *fs)
{
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 - htonll(fs->currentp->sdb_component.addr_first);
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;
}
/* to "find" a device, open it, get the current offset, then close */
unsigned long sdbfs_find_name(struct sdbfs *fs, const char *name)
{
unsigned long offset;
int ret;
ret = sdbfs_open_name(fs, name);
if (ret < 0)
return (unsigned long)ret;
offset = fs->f_offset;
sdbfs_close(fs);
return offset;
}
unsigned long sdbfs_find_id(struct sdbfs *fs, uint64_t vid, uint32_t did)
{
unsigned long offset;
int ret;
ret = sdbfs_open_id(fs, vid, did);
if (ret < 0)
return (unsigned long)ret;
offset = fs->f_offset;
sdbfs_close(fs);
return offset;
}
/* 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
/*
* This supports both the Linux kernel and barebox, that is similar
* by design, and defines __KERNEL__ too.
*/
#ifdef __BAREBOX__
# include <errno.h>
#else /* really linux */
# include <linux/errno.h>
#endif
#include <linux/types.h>
#include <linux/string.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.
*
* If building for barebox, we miss the macro, but we are sure that
* we are picking the correct header, because the library is only built
* within the barebox source tree.
*/
#if !defined(DECLARE_BITMAP) && !defined(__BAREBOX__)
# 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 different environments, take care of them */
#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 */
#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.
* 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.. */
unsigned long blocksize;
unsigned long entrypoint;
unsigned long flags;
/* 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 *currentp;
struct sdb_device current_record;
unsigned long f_len;
unsigned long f_offset; /* start of file */
unsigned long read_offset; /* current location */
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;
};
/* Some flags are set by the user, some (convert32) by the library */
#define SDBFS_F_VERBOSE 0x0001 /* not really used yet */
#define SDBFS_F_CONVERT32 0x0002 /* swap SDB words as they are read */
#define SDBFS_F_ZEROBASED 0x0004 /* zero is a valid data pointer */
/* Defined in glue.c */
int sdbfs_dev_create(struct sdbfs *fs);
int sdbfs_dev_destroy(struct sdbfs *fs);
struct sdbfs *sdbfs_dev_find(const char *name);
unsigned long sdbfs_find_name(struct sdbfs *fs, const char *name);
unsigned long sdbfs_find_id(struct sdbfs *fs, uint64_t vid, uint32_t did);
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, void *buf, int count);
int sdbfs_fwrite(struct sdbfs *fs, int offset, void *buf, int count);
int sdbfs_ferase(struct sdbfs *fs, int offset, 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__ */
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