Add a build-id to the kernel
[akaros.git] / kern / drivers / dev / version.c
1 /* Copyright (c) 2015 Google Inc
2  * Davide Libenzi <dlibenzi@google.com>
3  * See LICENSE for details.
4  */
5
6 #include <ros/common.h>
7 #include <ros/errno.h>
8 #include <smp.h>
9 #include <ns.h>
10 #include <kmalloc.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include <assert.h>
14 #include <err.h>
15 #include <build_info.h>
16
17 enum {
18         Kverdirqid = 0,
19         Kverbuildid,
20         Kverdate,
21         Kvercommitid,
22         Kverversion,
23         Kverversionname,
24         BUILD_ID_SZ = 20,
25         BUILD_ID_OFFSET = 16,
26 };
27
28 struct dev verdevtab;
29 static struct dirtab vertab[] = {
30         {".",                           {Kverdirqid,            0, QTDIR}, 0,   DMDIR|0550},
31         {"build_id",            {Kverbuildid},          0,      0444},
32         {"date",                        {Kverdate},                     0,      0444},
33         {"commitid",            {Kvercommitid},         0,      0444},
34         {"version",                     {Kverversion},          0,      0444},
35         {"version_name",        {Kverversionname},      0,      0444},
36 };
37
38 extern char __note_build_id_start[];
39 extern char __note_build_id_end[];
40
41 static char *get_build_id_start(void)
42 {
43         return __note_build_id_start + BUILD_ID_OFFSET;
44 }
45
46 static size_t build_id_sz(void)
47 {
48         return __note_build_id_end - get_build_id_start();
49 }
50
51 static long ver_emit_nlstr(char *dest, const char *src, long size,
52                                                    long offset)
53 {
54         long n, slen = strlen(src);
55         char *buf = kmalloc(slen + 1, MEM_WAIT);
56
57         snprintf(buf, slen + 1, "%s", src);
58         n = readmem(offset, dest, size, buf, slen + 1);
59         kfree(buf);
60
61         return n;
62 }
63
64 static size_t ver_get_file_size(const char *src)
65 {
66         if (!src)
67                 return 0;
68         return strlen(src) + 1;
69 }
70
71 static struct chan *ver_attach(char *spec)
72 {
73         return devattach(verdevtab.name, spec);
74 }
75
76 static void ver_init(void)
77 {
78         /* Our devtab's length params are wrong - need to stitch them up. */
79         vertab[Kverbuildid].length = build_id_sz();
80         vertab[Kverdate].length = ver_get_file_size(build_info_date);
81         vertab[Kvercommitid].length = ver_get_file_size(build_info_commitid);
82         vertab[Kverversion].length = ver_get_file_size(build_info_version);
83         vertab[Kverversionname].length = ver_get_file_size(build_info_version_name);
84 }
85
86 static void ver_shutdown(void)
87 {
88
89 }
90
91 static struct walkqid *ver_walk(struct chan *c, struct chan *nc, char **name,
92                                                                  int nname)
93 {
94         return devwalk(c, nc, name, nname, vertab, ARRAY_SIZE(vertab), devgen);
95 }
96
97 static int ver_stat(struct chan *c, uint8_t *db, int n)
98 {
99         return devstat(c, db, n, vertab, ARRAY_SIZE(vertab), devgen);
100 }
101
102 static struct chan *ver_open(struct chan *c, int omode)
103 {
104         if (c->qid.type & QTDIR) {
105                 if (openmode(omode) != O_READ)
106                         error(EPERM, ERROR_FIXME);
107         }
108         c->mode = openmode(omode);
109         c->flag |= COPEN;
110         c->offset = 0;
111         return c;
112 }
113
114 static void ver_close(struct chan *c)
115 {
116
117 }
118
119 /* Returns a char representing the lowest 4 bits of x */
120 static char num_to_nibble(int x)
121 {
122         return "0123456789abcdef"[x % 16];
123 }
124
125 static ssize_t read_buildid(void *va, long n, off64_t off)
126 {
127         /* Each build_id byte needs 2 chars, and 1 for the \0 */
128         char build_id[BUILD_ID_SZ * 2 + 1] = {0};
129         uint8_t hi, lo;
130         uint8_t *b = (uint8_t*)get_build_id_start();
131
132         for (int i = 0; i < BUILD_ID_SZ; i++) {
133                 hi = *b >> 4;
134                 lo = *b & 0xf;
135                 build_id[i * 2 + 0] = num_to_nibble(hi);
136                 build_id[i * 2 + 1] = num_to_nibble(lo);
137                 b++;
138         }
139         return readmem(off, va, n, build_id, sizeof(build_id));
140 }
141
142 static long ver_read(struct chan *c, void *va, long n, int64_t off)
143 {
144         switch ((int) c->qid.path) {
145         case Kverdirqid:
146                 return devdirread(c, va, n, vertab, ARRAY_SIZE(vertab), devgen);
147         case Kverbuildid:
148                 return read_buildid(va, n, off);
149         case Kverdate:
150                 if (build_info_date)
151                         return ver_emit_nlstr(va, build_info_date, n, (long) off);
152                 break;
153         case Kvercommitid:
154                 if (build_info_commitid)
155                         return ver_emit_nlstr(va, build_info_commitid, n, (long) off);
156                 break;
157         case Kverversion:
158                 if (build_info_version)
159                         return ver_emit_nlstr(va, build_info_version, n, (long) off);
160                 break;
161         case Kverversionname:
162                 if (build_info_version_name)
163                         return ver_emit_nlstr(va, build_info_version_name, n, (long) off);
164                 break;
165         default:
166                 error(EINVAL, ERROR_FIXME);
167         }
168
169         return 0;
170 }
171
172 static long ver_write(struct chan *c, void *a, long n, int64_t unused)
173 {
174         error(ENOTSUP, ERROR_FIXME);
175         return -1;
176 }
177
178 struct dev verdevtab __devtab = {
179         .name = "version",
180
181         .reset = devreset,
182         .init = ver_init,
183         .shutdown = ver_shutdown,
184         .attach = ver_attach,
185         .walk = ver_walk,
186         .stat = ver_stat,
187         .open = ver_open,
188         .create = devcreate,
189         .close = ver_close,
190         .read = ver_read,
191         .bread = devbread,
192         .write = ver_write,
193         .bwrite = devbwrite,
194         .remove = devremove,
195         .wstat = devwstat,
196 };