Export CONFIG_ options via #version/kconfig
[akaros.git] / kern / drivers / dev / vars.c
1 /* Copyright (c) 2015 Google Inc
2  * Barret Rhoden <brho@cs.berkeley.edu>
3  * See LICENSE for details.
4  *
5  * #vars device, exports read access to select kernel variables.  These
6  * variables are statically set.
7  *
8  * To add a variable, add a DEVVARS_ENTRY(name, format) somewhere in the kernel.
9  * The format is a string consisting of two characters, using a modified version
10  * of QEMU's formatting rules (ignoring count): [data_format][size]
11  *
12  * data_format is:
13  *     x (hex)
14  *     d (decimal)
15  *     u (unsigned)
16  *     o (octal)
17  *     c (char)                         does not need a size
18  *     s (string)                       does not need a size
19  * size is:
20  *     b (8 bits)
21  *     h (16 bits)
22  *     w (32 bits)
23  *     g (64 bits)
24  *
25  * e.g. DEVVARS_ENTRY(num_cores, "dw");
26  *
27  * Another thing we can consider doing is implementing create() to add variables
28  * on the fly.  We can easily get the address (symbol table), but not the type,
29  * unless we get debugging info.  We could consider a CTL command to allow the
30  * user to change the type, though that might overload write() if we also allow
31  * setting variables. */
32
33 #include <ns.h>
34 #include <kmalloc.h>
35 #include <kref.h>
36 #include <atomic.h>
37 #include <string.h>
38 #include <stdio.h>
39 #include <assert.h>
40 #include <error.h>
41 #include <sys/queue.h>
42 #include <fdtap.h>
43 #include <syscall.h>
44
45 struct dev vars_devtab;
46
47 static char *devname(void)
48 {
49         return vars_devtab.name;
50 }
51
52 static struct dirtab *vars_dir;
53 static size_t nr_vars;
54 static qlock_t vars_lock;
55
56 struct dirtab __attribute__((__section__("devvars")))
57               __devvars_dot = {".", {0, 0, QTDIR}, 0, DMDIR | 0555};
58
59 DEVVARS_ENTRY(num_cores, "dw");
60
61 static bool var_is_valid(struct dirtab *dir)
62 {
63         return dir->qid.vers != -1;
64 }
65
66 /* Careful with this.  c->name->s is the full path, at least sometimes. */
67 static struct dirtab *find_var_by_name(const char *name)
68 {
69         for (size_t i = 0; i < nr_vars; i++)
70                 if (!strcmp(vars_dir[i].name, name))
71                         return &vars_dir[i];
72         return 0;
73 }
74
75 static void vars_init(void)
76 {
77         /* If you name a section without a '.', GCC will create start and stop
78          * symbols, e.g. __start_SECTION */
79         extern struct dirtab __start_devvars;
80         extern struct dirtab __stop_devvars;
81         struct dirtab *dot, temp;
82
83         nr_vars = &__stop_devvars - &__start_devvars;
84         vars_dir = kmalloc_array(nr_vars, sizeof(struct dirtab), MEM_WAIT);
85         if (!vars_dir)
86                 error(ENOMEM, "kmalloc_array failed, nr_vars was %p", nr_vars);
87         memcpy(vars_dir, &__start_devvars, nr_vars * sizeof(struct dirtab));
88         /* "." needs to be the first entry in a devtable.  It might already be
89          * first, but we can do the swap regardless. */
90         temp = vars_dir[0];
91         dot = find_var_by_name(".");
92         assert(dot);
93         vars_dir[0] = *dot;
94         *dot = temp;
95         qlock_init(&vars_lock);
96 }
97
98 static struct chan *vars_attach(char *spec)
99 {
100         struct chan *c;
101
102         c = devattach(devname(), spec);
103         mkqid(&c->qid, 0, 0, QTDIR);
104         return c;
105 }
106
107 static struct walkqid *vars_walk(struct chan *c, struct chan *nc, char **name,
108                                                                  int nname)
109 {
110         ERRSTACK(1);
111         struct walkqid *ret;
112
113         qlock(&vars_lock);
114         if (waserror()) {
115                 qunlock(&vars_lock);
116                 nexterror();
117         }
118         ret = devwalk(c, nc, name, nname, vars_dir, nr_vars, devgen);
119         poperror();
120         qunlock(&vars_lock);
121         return ret;
122 }
123
124 static int vars_stat(struct chan *c, uint8_t *db, int n)
125 {
126         ERRSTACK(1);
127         int ret;
128
129         qlock(&vars_lock);
130         if (waserror()) {
131                 qunlock(&vars_lock);
132                 nexterror();
133         }
134         ret = devstat(c, db, n, vars_dir, nr_vars, devgen);
135         poperror();
136         qunlock(&vars_lock);
137         return ret;
138 }
139
140 static struct chan *vars_open(struct chan *c, int omode)
141 {
142         ERRSTACK(1);
143         struct chan *ret;
144
145         qlock(&vars_lock);
146         if (waserror()) {
147                 qunlock(&vars_lock);
148                 nexterror();
149         }
150         ret = devopen(c, omode, vars_dir, nr_vars, devgen);
151         poperror();
152         qunlock(&vars_lock);
153         return ret;
154 }
155
156 static void vars_close(struct chan *c)
157 {
158 }
159
160 static struct dirtab *find_free_var(void)
161 {
162         for (size_t i = 0; i < nr_vars; i++)
163                 if (!var_is_valid(&vars_dir[i]))
164                         return &vars_dir[i];
165         return 0;
166 }
167
168 /* We ignore the perm - they are all hard-coded in the dirtab */
169 static void vars_create(struct chan *c, char *name, int omode, uint32_t perm)
170 {
171         struct dirtab *new_slot;
172         uintptr_t addr;
173         char *bang;
174         size_t size;
175
176         /* TODO: check that the user is privileged */
177         bang = strchr(name, '!');
178         if (!bang)
179                 error(EINVAL, "Var %s has no ! in its format string", name);
180         *bang = 0;
181         addr = get_symbol_addr(name);
182         *bang = '!';
183         if (!addr)
184                 error(EINVAL, "Could not find symbol for %s", name);
185         bang++;
186         /* Note that we don't check the symbol type against the format.  We're
187          * trusting the user here.  o/w we'd need dwarf support. */
188         switch (*bang) {
189         case 'c':
190                 size = sizeof(char);
191                 break;
192         case 's':
193                 size = sizeof(char*);
194                 break;
195         case 'd':
196         case 'x':
197         case 'u':
198         case 'o':
199                 bang++;
200                 switch (*bang) {
201                 case 'b':
202                         size = sizeof(uint8_t);
203                         break;
204                 case 'h':
205                         size = sizeof(uint16_t);
206                         break;
207                 case 'w':
208                         size = sizeof(uint32_t);
209                         break;
210                 case 'g':
211                         size = sizeof(uint64_t);
212                         break;
213                 default:
214                         error(EINVAL, "Bad var size '%c'", *bang);
215                 }
216                 break;
217         default:
218                 error(EINVAL, "Unknown var data_format '%c'", *bang);
219         }
220         bang++;
221         if (*bang)
222                 error(EINVAL, "Extra chars for var %s", name);
223
224         qlock(&vars_lock);
225         new_slot = find_free_var();
226         if (!new_slot) {
227                 vars_dir = kreallocarray(vars_dir, nr_vars * 2, sizeof(struct dirtab),
228                                          MEM_WAIT);
229                 if (!vars_dir)
230                         error(ENOMEM, "krealloc_array failed, nr_vars was %p", nr_vars);
231                 memset(vars_dir + nr_vars, 0, nr_vars * sizeof(struct dirtab));
232                 for (size_t i = nr_vars; i < nr_vars * 2; i++)
233                         vars_dir[i].qid.vers = -1;
234                 new_slot = vars_dir + nr_vars;
235                 nr_vars *= 2;
236         }
237         strncpy(new_slot->name, name, sizeof(new_slot->name));
238         new_slot->qid.path = addr;
239         new_slot->qid.vers = 0;
240         new_slot->qid.type = QTFILE;
241         new_slot->length = size;
242         new_slot->perm = 0444;
243         c->qid = new_slot->qid;         /* need to update c with its new qid */
244         qunlock(&vars_lock);
245         c->mode = openmode(omode);
246 }
247
248 static const char *get_integer_fmt(char data_fmt, char data_size)
249 {
250         switch (data_fmt) {
251         case 'x':
252                 switch (data_size) {
253                 case 'b':
254                 case 'h':
255                 case 'w':
256                         return "0x%x";
257                 case 'g':
258                         return "0x%lx";
259                 }
260         case 'd':
261                 switch (data_size) {
262                 case 'b':
263                 case 'h':
264                 case 'w':
265                         return "%d";
266                 case 'g':
267                         return "%ld";
268                 }
269         case 'u':
270                 switch (data_size) {
271                 case 'b':
272                 case 'h':
273                 case 'w':
274                         return "%u";
275                 case 'g':
276                         return "%lu";
277                 }
278         case 'o':
279                 switch (data_size) {
280                 case 'b':
281                 case 'h':
282                 case 'w':
283                         return "0%o";
284                 case 'g':
285                         return "0%lo";
286                 }
287         }
288         return 0;
289 }
290
291 static long vars_read(struct chan *c, void *ubuf, long n, int64_t offset)
292 {
293         ERRSTACK(1);
294         char tmp[128];  /* big enough for any number and most strings */
295         size_t size = sizeof(tmp);
296         char data_size, data_fmt, *fmt;
297         const char *fmt_int;
298         bool is_signed = FALSE;
299         long ret;
300
301         qlock(&vars_lock);
302         if (waserror()) {
303                 qunlock(&vars_lock);
304                 nexterror();
305         }
306
307         if (c->qid.type == QTDIR) {
308                 ret = devdirread(c, ubuf, n, vars_dir, nr_vars, devgen);
309                 poperror();
310                 qunlock(&vars_lock);
311                 return ret;
312         }
313
314         /* These checks are mostly for the static variables.  They are a
315          * double-check for the user-provided vars. */
316         fmt = strchr(c->name->s, '!');
317         if (!fmt)
318                 error(EINVAL, "var %s has no ! in its format string", c->name->s);
319         fmt++;
320         data_fmt = *fmt;
321         if (!data_fmt)
322                 error(EINVAL, "var %s has no data_format", c->name->s);
323
324         switch (data_fmt) {
325         case 'c':
326                 size = snprintf(tmp, size, "%c", *(char*)c->qid.path);
327                 break;
328         case 's':
329                 size = snprintf(tmp, size, "%s", *(char**)c->qid.path);
330                 break;
331         case 'd':
332                 is_signed = TRUE;
333                 /* fall through */
334         case 'x':
335         case 'u':
336         case 'o':
337                 fmt++;
338                 data_size = *fmt;
339                 if (!data_size)
340                         error(EINVAL, "var %s has no size", c->name->s);
341                 fmt_int = get_integer_fmt(data_fmt, data_size);
342                 if (!fmt_int)
343                         error(EINVAL, "#%s was unable to get an int fmt for %s",
344                               devname(), c->name->s);
345                 switch (data_size) {
346                 case 'b':
347                         if (is_signed)
348                                 size = snprintf(tmp, size, fmt_int, *(int8_t*)c->qid.path);
349                         else
350                                 size = snprintf(tmp, size, fmt_int, *(uint8_t*)c->qid.path);
351                         break;
352                 case 'h':
353                         if (is_signed)
354                                 size = snprintf(tmp, size, fmt_int, *(int16_t*)c->qid.path);
355                         else
356                                 size = snprintf(tmp, size, fmt_int, *(uint16_t*)c->qid.path);
357                         break;
358                 case 'w':
359                         if (is_signed)
360                                 size = snprintf(tmp, size, fmt_int, *(int32_t*)c->qid.path);
361                         else
362                                 size = snprintf(tmp, size, fmt_int, *(uint32_t*)c->qid.path);
363                         break;
364                 case 'g':
365                         if (is_signed)
366                                 size = snprintf(tmp, size, fmt_int, *(int64_t*)c->qid.path);
367                         else
368                                 size = snprintf(tmp, size, fmt_int, *(uint64_t*)c->qid.path);
369                         break;
370                 default:
371                         error(EINVAL, "Bad #%s size %c", devname(), data_size);
372                 }
373                 break;
374         default:
375                 error(EINVAL, "Unknown #%s data_format %c", devname(), data_fmt);
376         }
377         fmt++;
378         if (*fmt)
379                 error(EINVAL, "Extra characters after var %s", c->name->s);
380         ret = readmem(offset, ubuf, n, tmp, size + 1);
381         poperror();
382         qunlock(&vars_lock);
383         return ret;
384 }
385
386 static long vars_write(struct chan *c, void *ubuf, long n, int64_t offset)
387 {
388         error(EFAIL, "Can't write to a #%s file", devname());
389 }
390
391 /* remove is interesting.  we mark the qid in the dirtab as -1, which is a
392  * signal to devgen that it is an invalid entry.  someone could already have
393  * done a walk (before we qlocked) and grabbed the qid before it was -1.  as far
394  * as they are concerned, they have a valid entry, since "the qid is the file"
395  * for devvars.  (i.e. the chan gets a copy of the entire file, which fits into
396  * the qid). */
397 static void vars_remove(struct chan *c)
398 {
399         ERRSTACK(1);
400         struct dirtab *dir;
401         char *dir_name;
402
403         /* chan's name may have multiple elements in the path; get the last one. */
404         dir_name = strrchr(c->name->s, '/');
405         dir_name = dir_name ? dir_name + 1 : c->name->s;
406
407         qlock(&vars_lock);
408         if (waserror()) {
409                 qunlock(&vars_lock);
410                 nexterror();
411         }
412         dir = find_var_by_name(dir_name);
413         if (!dir || dir->qid.vers == -1)
414                 error(ENOENT, "Failed to remove %s, was it already removed?",
415                       c->name->s);
416         dir->qid.vers = -1;
417         poperror();
418         qunlock(&vars_lock);
419 }
420
421 struct dev vars_devtab __devtab = {
422         .name = "vars",
423         .reset = devreset,
424         .init = vars_init,
425         .shutdown = devshutdown,
426         .attach = vars_attach,
427         .walk = vars_walk,
428         .stat = vars_stat,
429         .open = vars_open,
430         .create = vars_create,
431         .close = vars_close,
432         .read = vars_read,
433         .bread = devbread,
434         .write = vars_write,
435         .bwrite = devbwrite,
436         .remove = vars_remove,
437         .wstat = devwstat,
438         .power = devpower,
439         .chaninfo = devchaninfo,
440         .tapfd = 0,
441 };
442
443 /* The utest needs these variables exported */
444 #ifdef CONFIG_DEVVARS_TEST
445
446 static char *s = "string";
447 static char c = 'x';
448 static uint8_t  u8  = 8;
449 static uint16_t u16 = 16;
450 static uint32_t u32 = 32;
451 static uint64_t u64 = 64;
452 static uint8_t  d8  = -8;
453 static uint16_t d16 = -16;
454 static uint32_t d32 = -32;
455 static uint64_t d64 = -64;
456 static uint8_t  x8  = 0x8;
457 static uint16_t x16 = 0x16;
458 static uint32_t x32 = 0x32;
459 static uint64_t x64 = 0x64;
460 static uint8_t  o8  = 01;
461 static uint16_t o16 = 016;
462 static uint32_t o32 = 032;
463 static uint64_t o64 = 064;
464
465 /* For testing creation.  There is no ENTRY for this. */
466 char *devvars_foobar = "foobar";
467
468 DEVVARS_ENTRY(s, "s");
469 DEVVARS_ENTRY(c, "c");
470 DEVVARS_ENTRY(u8,  "ub");
471 DEVVARS_ENTRY(u16, "uh");
472 DEVVARS_ENTRY(u32, "uw");
473 DEVVARS_ENTRY(u64, "ug");
474 DEVVARS_ENTRY(d8,  "db");
475 DEVVARS_ENTRY(d16, "dh");
476 DEVVARS_ENTRY(d32, "dw");
477 DEVVARS_ENTRY(d64, "dg");
478 DEVVARS_ENTRY(x8,  "xb");
479 DEVVARS_ENTRY(x16, "xh");
480 DEVVARS_ENTRY(x32, "xw");
481 DEVVARS_ENTRY(x64, "xg");
482 DEVVARS_ENTRY(o8,  "ob");
483 DEVVARS_ENTRY(o16, "oh");
484 DEVVARS_ENTRY(o32, "ow");
485 DEVVARS_ENTRY(o64, "og");
486
487 #endif /* CONFIG_DEVVARS_TEST */