blob: d083eb412b8a73c1d50fcd178239436009d930e0 [file] [log] [blame]
/* Copyright (c) 2015 Google Inc
* Davide Libenzi <dlibenzi@google.com>
* See LICENSE for details.
*/
#include <ros/common.h>
#include <ros/errno.h>
#include <smp.h>
#include <ns.h>
#include <kmalloc.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <err.h>
#include <build_info.h>
enum {
Kverdirqid = 0,
Kverbuildid,
Kverdate,
Kvercommitid,
Kverversion,
Kverversionname,
Kverkconfig,
BUILD_ID_SZ = 20,
BUILD_ID_OFFSET = 16,
};
struct dev verdevtab;
static struct dirtab vertab[] = {
{".", {Kverdirqid, 0, QTDIR}, 0, DMDIR|0550},
{"build_id", {Kverbuildid}, 0, 0444},
{"date", {Kverdate}, 0, 0444},
{"commitid", {Kvercommitid}, 0, 0444},
{"version", {Kverversion}, 0, 0444},
{"version_name",{Kverversionname}, 0, 0444},
{"kconfig", {Kverkconfig}, 0, 0444},
};
extern char __note_build_id_start[];
extern char __note_build_id_end[];
extern const char *__kconfig_str;
static char *get_build_id_start(void)
{
return __note_build_id_start + BUILD_ID_OFFSET;
}
static size_t build_id_sz(void)
{
return __note_build_id_end - get_build_id_start();
}
static long ver_emit_nlstr(char *dest, const char *src, long size,
long offset)
{
long n, slen = strlen(src);
char *buf = kmalloc(slen + 1, MEM_WAIT);
snprintf(buf, slen + 1, "%s", src);
n = readmem(offset, dest, size, buf, slen + 1);
kfree(buf);
return n;
}
static size_t ver_get_file_size(const char *src)
{
if (!src)
return 0;
return strlen(src) + 1;
}
static struct chan *ver_attach(char *spec)
{
return devattach(verdevtab.name, spec);
}
static void ver_init(void)
{
/* Our devtab's length params are wrong - need to stitch them up. */
vertab[Kverbuildid].length = build_id_sz();
vertab[Kverdate].length = ver_get_file_size(build_info_date);
vertab[Kvercommitid].length = ver_get_file_size(build_info_commitid);
vertab[Kverversion].length = ver_get_file_size(build_info_version);
vertab[Kverversionname].length =
ver_get_file_size(build_info_version_name);
vertab[Kverkconfig].length = strlen(__kconfig_str) + 1;
}
static void ver_shutdown(void)
{
}
static struct walkqid *ver_walk(struct chan *c, struct chan *nc, char **name,
unsigned int nname)
{
return devwalk(c, nc, name, nname, vertab, ARRAY_SIZE(vertab), devgen);
}
static size_t ver_stat(struct chan *c, uint8_t *db, size_t n)
{
return devstat(c, db, n, vertab, ARRAY_SIZE(vertab), devgen);
}
static struct chan *ver_open(struct chan *c, int omode)
{
if (c->qid.type & QTDIR) {
if (openmode(omode) != O_READ)
error(EPERM, ERROR_FIXME);
}
c->mode = openmode(omode);
c->flag |= COPEN;
c->offset = 0;
return c;
}
static void ver_close(struct chan *c)
{
}
/* Returns a char representing the lowest 4 bits of x */
static char num_to_nibble(unsigned int x)
{
return "0123456789abcdef"[x & 0xf];
}
static ssize_t read_buildid(void *va, long n, off64_t off)
{
/* Each build_id byte needs 2 chars, and 1 for the \0 */
char build_id[BUILD_ID_SZ * 2 + 1] = {0};
uint8_t hi, lo;
uint8_t *b = (uint8_t*)get_build_id_start();
for (int i = 0; i < BUILD_ID_SZ; i++) {
hi = *b >> 4;
lo = *b & 0xf;
build_id[i * 2 + 0] = num_to_nibble(hi);
build_id[i * 2 + 1] = num_to_nibble(lo);
b++;
}
return readmem(off, va, n, build_id, sizeof(build_id));
}
static size_t ver_read(struct chan *c, void *va, size_t n, off64_t off)
{
switch ((int) c->qid.path) {
case Kverdirqid:
return devdirread(c, va, n, vertab, ARRAY_SIZE(vertab), devgen);
case Kverbuildid:
return read_buildid(va, n, off);
case Kverdate:
if (build_info_date)
return ver_emit_nlstr(va, build_info_date, n,
(long) off);
break;
case Kvercommitid:
if (build_info_commitid)
return ver_emit_nlstr(va, build_info_commitid, n,
(long) off);
break;
case Kverversion:
if (build_info_version)
return ver_emit_nlstr(va, build_info_version, n,
(long) off);
break;
case Kverversionname:
if (build_info_version_name)
return ver_emit_nlstr(va, build_info_version_name, n,
(long) off);
break;
case Kverkconfig:
return readstr(off, va, n, __kconfig_str);
default:
error(EINVAL, ERROR_FIXME);
}
return 0;
}
static size_t ver_write(struct chan *c, void *a, size_t n, off64_t unused)
{
error(ENOTSUP, ERROR_FIXME);
return -1;
}
struct dev verdevtab __devtab = {
.name = "version",
.reset = devreset,
.init = ver_init,
.shutdown = ver_shutdown,
.attach = ver_attach,
.walk = ver_walk,
.stat = ver_stat,
.open = ver_open,
.create = devcreate,
.close = ver_close,
.read = ver_read,
.bread = devbread,
.write = ver_write,
.bwrite = devbwrite,
.remove = devremove,
.wstat = devwstat,
};