diff --git a/sdb-lib/Makefile b/sdb-lib/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..469967349ea267e5e8ae9c9a24c87b31cb455d56 --- /dev/null +++ b/sdb-lib/Makefile @@ -0,0 +1,43 @@ + +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 -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: diff --git a/sdb-lib/Makefile.arch b/sdb-lib/Makefile.arch new file mode 100644 index 0000000000000000000000000000000000000000..12ca01c5d1cda0b2aa2c0c04ac0d68147289fd7d --- /dev/null +++ b/sdb-lib/Makefile.arch @@ -0,0 +1,58 @@ + +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 diff --git a/sdb-lib/access.c b/sdb-lib/access.c new file mode 100644 index 0000000000000000000000000000000000000000..9b47ecb66a92c6e3e95c5f6be1f2610b03c9970e --- /dev/null +++ b/sdb-lib/access.c @@ -0,0 +1,58 @@ +/* + * 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 = 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 + 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 = 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 + ret = fs->write(fs, fs->f_offset + offset, buf, count); + if (ret > 0) + fs->read_offset = offset + ret; + return ret; +} diff --git a/sdb-lib/check-endian b/sdb-lib/check-endian new file mode 100755 index 0000000000000000000000000000000000000000..0c2acff5342d14a0d15cb82c7377f7d051cadd4e --- /dev/null +++ b/sdb-lib/check-endian @@ -0,0 +1,24 @@ +#!/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 diff --git a/sdb-lib/glue.c b/sdb-lib/glue.c new file mode 100644 index 0000000000000000000000000000000000000000..4a57bfc1c46bc5fa8ee33d64eebc631798206adc --- /dev/null +++ b/sdb-lib/glue.c @@ -0,0 +1,159 @@ +/* + * 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); + if (!fs->read) + return NULL; + 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; +} + diff --git a/sdb-lib/libsdbfs-freestanding.h b/sdb-lib/libsdbfs-freestanding.h new file mode 100644 index 0000000000000000000000000000000000000000..3f7868df9e1e018a660add51a6a4b5315f5ba9f2 --- /dev/null +++ b/sdb-lib/libsdbfs-freestanding.h @@ -0,0 +1,19 @@ + +/* 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 diff --git a/sdb-lib/libsdbfs-kernel.h b/sdb-lib/libsdbfs-kernel.h new file mode 100644 index 0000000000000000000000000000000000000000..16e359e852e14b65076398f9c70f75467b556f38 --- /dev/null +++ b/sdb-lib/libsdbfs-kernel.h @@ -0,0 +1,24 @@ + +#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__) diff --git a/sdb-lib/libsdbfs-user.h b/sdb-lib/libsdbfs-user.h new file mode 100644 index 0000000000000000000000000000000000000000..d54961c4c2e0633e73ad785788f907c113b63f16 --- /dev/null +++ b/sdb-lib/libsdbfs-user.h @@ -0,0 +1,15 @@ +#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__ */ diff --git a/sdb-lib/libsdbfs.h b/sdb-lib/libsdbfs.h new file mode 100644 index 0000000000000000000000000000000000000000..698c7703c343c127cdbe95a36bd35af4694cd3d7 --- /dev/null +++ b/sdb-lib/libsdbfs.h @@ -0,0 +1,81 @@ +#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, 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) +{ + 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__ */