vmm: refactor userspace's emsr_fakewrite()
[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         Kverkconfig,
25         BUILD_ID_SZ = 20,
26         BUILD_ID_OFFSET = 16,
27 };
28
29 struct dev verdevtab;
30 static struct dirtab vertab[] = {
31         {".",           {Kverdirqid, 0, QTDIR}, 0,      DMDIR|0550},
32         {"build_id",    {Kverbuildid},          0,      0444},
33         {"date",        {Kverdate},             0,      0444},
34         {"commitid",    {Kvercommitid},         0,      0444},
35         {"version",     {Kverversion},          0,      0444},
36         {"version_name",{Kverversionname},      0,      0444},
37         {"kconfig",     {Kverkconfig},          0,      0444},
38 };
39
40 extern char __note_build_id_start[];
41 extern char __note_build_id_end[];
42
43 extern const char *__kconfig_str;
44
45 static char *get_build_id_start(void)
46 {
47         return __note_build_id_start + BUILD_ID_OFFSET;
48 }
49
50 static size_t build_id_sz(void)
51 {
52         return __note_build_id_end - get_build_id_start();
53 }
54
55 static long ver_emit_nlstr(char *dest, const char *src, long size,
56                                                    long offset)
57 {
58         long n, slen = strlen(src);
59         char *buf = kmalloc(slen + 1, MEM_WAIT);
60
61         snprintf(buf, slen + 1, "%s", src);
62         n = readmem(offset, dest, size, buf, slen + 1);
63         kfree(buf);
64
65         return n;
66 }
67
68 static size_t ver_get_file_size(const char *src)
69 {
70         if (!src)
71                 return 0;
72         return strlen(src) + 1;
73 }
74
75 static struct chan *ver_attach(char *spec)
76 {
77         return devattach(verdevtab.name, spec);
78 }
79
80 static void ver_init(void)
81 {
82         /* Our devtab's length params are wrong - need to stitch them up. */
83         vertab[Kverbuildid].length = build_id_sz();
84         vertab[Kverdate].length = ver_get_file_size(build_info_date);
85         vertab[Kvercommitid].length = ver_get_file_size(build_info_commitid);
86         vertab[Kverversion].length = ver_get_file_size(build_info_version);
87         vertab[Kverversionname].length =
88                 ver_get_file_size(build_info_version_name);
89         vertab[Kverkconfig].length = strlen(__kconfig_str) + 1;
90 }
91
92 static struct walkqid *ver_walk(struct chan *c, struct chan *nc, char **name,
93                                 unsigned int nname)
94 {
95         return devwalk(c, nc, name, nname, vertab, ARRAY_SIZE(vertab), devgen);
96 }
97
98 static size_t ver_stat(struct chan *c, uint8_t *db, size_t n)
99 {
100         return devstat(c, db, n, vertab, ARRAY_SIZE(vertab), devgen);
101 }
102
103 static struct chan *ver_open(struct chan *c, int omode)
104 {
105         if (c->qid.type & QTDIR) {
106                 if (openmode(omode) != O_READ)
107                         error(EPERM, ERROR_FIXME);
108         }
109         c->mode = openmode(omode);
110         c->flag |= COPEN;
111         c->offset = 0;
112         return c;
113 }
114
115 static void ver_close(struct chan *c)
116 {
117 }
118
119 /* Returns a char representing the lowest 4 bits of x */
120 static char num_to_nibble(unsigned int x)
121 {
122         return "0123456789abcdef"[x & 0xf];
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 size_t ver_read(struct chan *c, void *va, size_t n, off64_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,
152                                               (long) off);
153                 break;
154         case Kvercommitid:
155                 if (build_info_commitid)
156                         return ver_emit_nlstr(va, build_info_commitid, n,
157                                               (long) off);
158                 break;
159         case Kverversion:
160                 if (build_info_version)
161                         return ver_emit_nlstr(va, build_info_version, n,
162                                               (long) off);
163                 break;
164         case Kverversionname:
165                 if (build_info_version_name)
166                         return ver_emit_nlstr(va, build_info_version_name, n,
167                                               (long) off);
168                 break;
169         case Kverkconfig:
170                 return readstr(off, va, n, __kconfig_str);
171         default:
172                 error(EINVAL, ERROR_FIXME);
173         }
174
175         return 0;
176 }
177
178 static size_t ver_write(struct chan *c, void *a, size_t n, off64_t unused)
179 {
180         error(ENOTSUP, ERROR_FIXME);
181         return -1;
182 }
183
184 struct dev verdevtab __devtab = {
185         .name = "version",
186
187         .reset = devreset,
188         .init = ver_init,
189         .shutdown = devshutdown,
190         .attach = ver_attach,
191         .walk = ver_walk,
192         .stat = ver_stat,
193         .open = ver_open,
194         .create = devcreate,
195         .close = ver_close,
196         .read = ver_read,
197         .bread = devbread,
198         .write = ver_write,
199         .bwrite = devbwrite,
200         .remove = devremove,
201         .wstat = devwstat,
202 };