9ns: Fix devtab function pointer signatures
[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                                                                  unsigned 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 size_t vars_stat(struct chan *c, uint8_t *db, size_t n)
125 {
126         ERRSTACK(1);
127         size_t 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                         char *ext)
171 {
172         struct dirtab *new_slot;
173         uintptr_t addr;
174         char *bang;
175         size_t size;
176
177         if (perm & DMSYMLINK)
178                 error(EINVAL, "#%s doesn't support symlinks", devname());
179         /* TODO: check that the user is privileged */
180         bang = strchr(name, '!');
181         if (!bang)
182                 error(EINVAL, "Var %s has no ! in its format string", name);
183         *bang = 0;
184         addr = get_symbol_addr(name);
185         *bang = '!';
186         if (!addr)
187                 error(EINVAL, "Could not find symbol for %s", name);
188         bang++;
189         /* Note that we don't check the symbol type against the format.  We're
190          * trusting the user here.  o/w we'd need dwarf support. */
191         switch (*bang) {
192         case 'c':
193                 size = sizeof(char);
194                 break;
195         case 's':
196                 size = sizeof(char*);
197                 break;
198         case 'd':
199         case 'x':
200         case 'u':
201         case 'o':
202                 bang++;
203                 switch (*bang) {
204                 case 'b':
205                         size = sizeof(uint8_t);
206                         break;
207                 case 'h':
208                         size = sizeof(uint16_t);
209                         break;
210                 case 'w':
211                         size = sizeof(uint32_t);
212                         break;
213                 case 'g':
214                         size = sizeof(uint64_t);
215                         break;
216                 default:
217                         error(EINVAL, "Bad var size '%c'", *bang);
218                 }
219                 break;
220         default:
221                 error(EINVAL, "Unknown var data_format '%c'", *bang);
222         }
223         bang++;
224         if (*bang)
225                 error(EINVAL, "Extra chars for var %s", name);
226
227         qlock(&vars_lock);
228         new_slot = find_free_var();
229         if (!new_slot) {
230                 vars_dir = kreallocarray(vars_dir, nr_vars * 2, sizeof(struct dirtab),
231                                          MEM_WAIT);
232                 if (!vars_dir)
233                         error(ENOMEM, "krealloc_array failed, nr_vars was %p", nr_vars);
234                 memset(vars_dir + nr_vars, 0, nr_vars * sizeof(struct dirtab));
235                 for (size_t i = nr_vars; i < nr_vars * 2; i++)
236                         vars_dir[i].qid.vers = -1;
237                 new_slot = vars_dir + nr_vars;
238                 nr_vars *= 2;
239         }
240         strlcpy(new_slot->name, name, sizeof(new_slot->name));
241         new_slot->qid.path = addr;
242         new_slot->qid.vers = 0;
243         new_slot->qid.type = QTFILE;
244         new_slot->length = size;
245         new_slot->perm = 0444;
246         c->qid = new_slot->qid;         /* need to update c with its new qid */
247         qunlock(&vars_lock);
248         c->mode = openmode(omode);
249 }
250
251 static const char *get_integer_fmt(char data_fmt, char data_size)
252 {
253         switch (data_fmt) {
254         case 'x':
255                 switch (data_size) {
256                 case 'b':
257                 case 'h':
258                 case 'w':
259                         return "0x%x";
260                 case 'g':
261                         return "0x%lx";
262                 }
263         case 'd':
264                 switch (data_size) {
265                 case 'b':
266                 case 'h':
267                 case 'w':
268                         return "%d";
269                 case 'g':
270                         return "%ld";
271                 }
272         case 'u':
273                 switch (data_size) {
274                 case 'b':
275                 case 'h':
276                 case 'w':
277                         return "%u";
278                 case 'g':
279                         return "%lu";
280                 }
281         case 'o':
282                 switch (data_size) {
283                 case 'b':
284                 case 'h':
285                 case 'w':
286                         return "0%o";
287                 case 'g':
288                         return "0%lo";
289                 }
290         }
291         return 0;
292 }
293
294 static size_t vars_read(struct chan *c, void *ubuf, size_t n, off64_t offset)
295 {
296         ERRSTACK(1);
297         char tmp[128];  /* big enough for any number and most strings */
298         size_t size = sizeof(tmp);
299         char data_size, data_fmt, *fmt;
300         const char *fmt_int;
301         bool is_signed = FALSE;
302         size_t ret;
303
304         qlock(&vars_lock);
305         if (waserror()) {
306                 qunlock(&vars_lock);
307                 nexterror();
308         }
309
310         if (c->qid.type == QTDIR) {
311                 ret = devdirread(c, ubuf, n, vars_dir, nr_vars, devgen);
312                 poperror();
313                 qunlock(&vars_lock);
314                 return ret;
315         }
316
317         /* These checks are mostly for the static variables.  They are a
318          * double-check for the user-provided vars. */
319         fmt = strchr(c->name->s, '!');
320         if (!fmt)
321                 error(EINVAL, "var %s has no ! in its format string", c->name->s);
322         fmt++;
323         data_fmt = *fmt;
324         if (!data_fmt)
325                 error(EINVAL, "var %s has no data_format", c->name->s);
326
327         switch (data_fmt) {
328         case 'c':
329                 size = snprintf(tmp, size, "%c", *(char*)c->qid.path);
330                 break;
331         case 's':
332                 size = snprintf(tmp, size, "%s", *(char**)c->qid.path);
333                 break;
334         case 'd':
335                 is_signed = TRUE;
336                 /* fall through */
337         case 'x':
338         case 'u':
339         case 'o':
340                 fmt++;
341                 data_size = *fmt;
342                 if (!data_size)
343                         error(EINVAL, "var %s has no size", c->name->s);
344                 fmt_int = get_integer_fmt(data_fmt, data_size);
345                 if (!fmt_int)
346                         error(EINVAL, "#%s was unable to get an int fmt for %s",
347                               devname(), c->name->s);
348                 switch (data_size) {
349                 case 'b':
350                         if (is_signed)
351                                 size = snprintf(tmp, size, fmt_int, *(int8_t*)c->qid.path);
352                         else
353                                 size = snprintf(tmp, size, fmt_int, *(uint8_t*)c->qid.path);
354                         break;
355                 case 'h':
356                         if (is_signed)
357                                 size = snprintf(tmp, size, fmt_int, *(int16_t*)c->qid.path);
358                         else
359                                 size = snprintf(tmp, size, fmt_int, *(uint16_t*)c->qid.path);
360                         break;
361                 case 'w':
362                         if (is_signed)
363                                 size = snprintf(tmp, size, fmt_int, *(int32_t*)c->qid.path);
364                         else
365                                 size = snprintf(tmp, size, fmt_int, *(uint32_t*)c->qid.path);
366                         break;
367                 case 'g':
368                         if (is_signed)
369                                 size = snprintf(tmp, size, fmt_int, *(int64_t*)c->qid.path);
370                         else
371                                 size = snprintf(tmp, size, fmt_int, *(uint64_t*)c->qid.path);
372                         break;
373                 default:
374                         error(EINVAL, "Bad #%s size %c", devname(), data_size);
375                 }
376                 break;
377         default:
378                 error(EINVAL, "Unknown #%s data_format %c", devname(), data_fmt);
379         }
380         fmt++;
381         if (*fmt)
382                 error(EINVAL, "Extra characters after var %s", c->name->s);
383         ret = readmem(offset, ubuf, n, tmp, size + 1);
384         poperror();
385         qunlock(&vars_lock);
386         return ret;
387 }
388
389 static size_t vars_write(struct chan *c, void *ubuf, size_t n, off64_t offset)
390 {
391         error(EFAIL, "Can't write to a #%s file", devname());
392 }
393
394 /* remove is interesting.  we mark the qid in the dirtab as -1, which is a
395  * signal to devgen that it is an invalid entry.  someone could already have
396  * done a walk (before we qlocked) and grabbed the qid before it was -1.  as far
397  * as they are concerned, they have a valid entry, since "the qid is the file"
398  * for devvars.  (i.e. the chan gets a copy of the entire file, which fits into
399  * the qid). */
400 static void vars_remove(struct chan *c)
401 {
402         ERRSTACK(1);
403         struct dirtab *dir;
404         char *dir_name;
405
406         /* chan's name may have multiple elements in the path; get the last one. */
407         dir_name = strrchr(c->name->s, '/');
408         dir_name = dir_name ? dir_name + 1 : c->name->s;
409
410         qlock(&vars_lock);
411         if (waserror()) {
412                 qunlock(&vars_lock);
413                 nexterror();
414         }
415         dir = find_var_by_name(dir_name);
416         if (!dir || dir->qid.vers == -1)
417                 error(ENOENT, "Failed to remove %s, was it already removed?",
418                       c->name->s);
419         dir->qid.vers = -1;
420         poperror();
421         qunlock(&vars_lock);
422 }
423
424 struct dev vars_devtab __devtab = {
425         .name = "vars",
426         .reset = devreset,
427         .init = vars_init,
428         .shutdown = devshutdown,
429         .attach = vars_attach,
430         .walk = vars_walk,
431         .stat = vars_stat,
432         .open = vars_open,
433         .create = vars_create,
434         .close = vars_close,
435         .read = vars_read,
436         .bread = devbread,
437         .write = vars_write,
438         .bwrite = devbwrite,
439         .remove = vars_remove,
440         .wstat = devwstat,
441         .power = devpower,
442         .chaninfo = devchaninfo,
443         .tapfd = 0,
444 };
445
446 /* The utest needs these variables exported */
447 #ifdef CONFIG_DEVVARS_TEST
448
449 static char *s = "string";
450 static char c = 'x';
451 static uint8_t  u8  = 8;
452 static uint16_t u16 = 16;
453 static uint32_t u32 = 32;
454 static uint64_t u64 = 64;
455 static uint8_t  d8  = -8;
456 static uint16_t d16 = -16;
457 static uint32_t d32 = -32;
458 static uint64_t d64 = -64;
459 static uint8_t  x8  = 0x8;
460 static uint16_t x16 = 0x16;
461 static uint32_t x32 = 0x32;
462 static uint64_t x64 = 0x64;
463 static uint8_t  o8  = 01;
464 static uint16_t o16 = 016;
465 static uint32_t o32 = 032;
466 static uint64_t o64 = 064;
467
468 /* For testing creation.  There is no ENTRY for this. */
469 char *devvars_foobar = "foobar";
470
471 DEVVARS_ENTRY(s, "s");
472 DEVVARS_ENTRY(c, "c");
473 DEVVARS_ENTRY(u8,  "ub");
474 DEVVARS_ENTRY(u16, "uh");
475 DEVVARS_ENTRY(u32, "uw");
476 DEVVARS_ENTRY(u64, "ug");
477 DEVVARS_ENTRY(d8,  "db");
478 DEVVARS_ENTRY(d16, "dh");
479 DEVVARS_ENTRY(d32, "dw");
480 DEVVARS_ENTRY(d64, "dg");
481 DEVVARS_ENTRY(x8,  "xb");
482 DEVVARS_ENTRY(x16, "xh");
483 DEVVARS_ENTRY(x32, "xw");
484 DEVVARS_ENTRY(x64, "xg");
485 DEVVARS_ENTRY(o8,  "ob");
486 DEVVARS_ENTRY(o16, "oh");
487 DEVVARS_ENTRY(o32, "ow");
488 DEVVARS_ENTRY(o64, "og");
489
490 #endif /* CONFIG_DEVVARS_TEST */