| /* 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, |
| }; |