Added explicit errno reporting from error() API.
[akaros.git] / kern / arch / x86 / devarch.c
1 /* 
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9
10 #include <vfs.h>
11 #include <kfs.h>
12 #include <slab.h>
13 #include <kmalloc.h>
14 #include <kref.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <error.h>
19 #include <cpio.h>
20 #include <pmap.h>
21 #include <smp.h>
22 #include <ip.h>
23 #include <time.h>
24
25 struct dev archdevtab;
26
27 static char *devname(void)
28 {
29         return archdevtab.name;
30 }
31
32 typedef struct IOMap IOMap;
33 struct IOMap {
34         IOMap *next;
35         int reserved;
36         char tag[13];
37         uint32_t start;
38         uint32_t end;
39 };
40
41 static struct {
42         spinlock_t lock;
43         IOMap *map;
44         IOMap *free;
45         IOMap maps[32];                         // some initial free maps
46
47         qlock_t ql;                                     // lock for reading map
48 } iomap;
49
50 enum {
51         Qdir = 0,
52         Qioalloc = 1,
53         Qiob,
54         Qiow,
55         Qiol,
56         Qgdb,
57         Qbase,
58         Qmapram,
59         Qrealmem,
60
61         Qmax = 16,
62 };
63
64 typedef long Rdwrfn(struct chan *, void *, long, int64_t);
65
66 static Rdwrfn *readfn[Qmax];
67 static Rdwrfn *writefn[Qmax];
68
69 static struct dirtab archdir[Qmax] = {
70         {".", {Qdir, 0, QTDIR}, 0, 0555},
71         {"ioalloc", {Qioalloc, 0}, 0, 0444},
72         {"iob", {Qiob, 0}, 0, 0666},
73         {"iow", {Qiow, 0}, 0, 0666},
74         {"iol", {Qiol, 0}, 0, 0666},
75         {"gdb", {Qgdb, 0}, 0, 0660},
76         {"mapram", {Qmapram, 0}, 0, 0444},
77         {"realmodemem", {Qrealmem, 0}, 0, 0660},
78 };
79
80 spinlock_t archwlock;                   /* the lock is only for changing archdir */
81 int narchdir = Qbase;
82 int gdbactive = 0;
83
84 /* If we use these, put this in a header */
85 int ioalloc(int port, int size, int align, char *tag);
86
87 /*
88  * Add a file to the #P listing.  Once added, you can't delete it.
89  * You can't add a file with the same name as one already there,
90  * and you get a pointer to the Dirtab entry so you can do things
91  * like change the Qid version.  Changing the Qid path is disallowed.
92  */
93 struct dirtab *addarchfile(char *name, int perm, Rdwrfn * rdfn, Rdwrfn * wrfn)
94 {
95         int i;
96         struct dirtab d;
97         struct dirtab *dp;
98
99         memset(&d, 0, sizeof d);
100         strncpy(d.name, name, sizeof(d.name));
101         d.perm = perm;
102
103         spin_lock(&archwlock);
104         if (narchdir >= Qmax) {
105                 spin_unlock(&archwlock);
106                 return NULL;
107         }
108
109         for (i = 0; i < narchdir; i++)
110                 if (strcmp(archdir[i].name, name) == 0) {
111                         spin_unlock(&archwlock);
112                         return NULL;
113                 }
114
115         d.qid.path = narchdir;
116         archdir[narchdir] = d;
117         readfn[narchdir] = rdfn;
118         writefn[narchdir] = wrfn;
119         dp = &archdir[narchdir++];
120         spin_unlock(&archwlock);
121
122         return dp;
123 }
124
125 void ioinit(void)
126 {
127         int i;
128         char *excluded = "";
129
130         panic("Akaros doesn't do IO port allocation yet.  Don't init.");
131         for (i = 0; i < ARRAY_SIZE(iomap.maps) - 1; i++)
132                 iomap.maps[i].next = &iomap.maps[i + 1];
133         iomap.maps[i].next = NULL;
134         iomap.free = iomap.maps;
135         char *s;
136
137         s = excluded;
138         while (s && *s != '\0' && *s != '\n') {
139                 char *ends;
140                 int io_s, io_e;
141
142                 io_s = (int)strtol(s, &ends, 0);
143                 if (ends == NULL || ends == s || *ends != '-') {
144                         printd("ioinit: cannot parse option string\n");
145                         break;
146                 }
147                 s = ++ends;
148
149                 io_e = (int)strtol(s, &ends, 0);
150                 if (ends && *ends == ',')
151                         *ends++ = '\0';
152                 s = ends;
153
154                 ioalloc(io_s, io_e - io_s + 1, 0, "pre-allocated");
155         }
156 }
157
158 // Reserve a range to be ioalloced later.
159 // This is in particular useful for exchangable cards, such
160 // as pcmcia and cardbus cards.
161 int ioreserve(int unused_int, int size, int align, char *tag)
162 {
163         IOMap *map, **l;
164         int i, port;
165
166         spin_lock(&(&iomap)->lock);
167         // find a free port above 0x400 and below 0x1000
168         port = 0x400;
169         for (l = &iomap.map; *l; l = &(*l)->next) {
170                 map = *l;
171                 if (map->start < 0x400)
172                         continue;
173                 i = map->start - port;
174                 if (i > size)
175                         break;
176                 if (align > 0)
177                         port = ((port + align - 1) / align) * align;
178                 else
179                         port = map->end;
180         }
181         if (*l == NULL) {
182                 spin_unlock(&(&iomap)->lock);
183                 return -1;
184         }
185         map = iomap.free;
186         if (map == NULL) {
187                 printd("ioalloc: out of maps");
188                 spin_unlock(&(&iomap)->lock);
189                 return port;
190         }
191         iomap.free = map->next;
192         map->next = *l;
193         map->start = port;
194         map->end = port + size;
195         map->reserved = 1;
196         strncpy(map->tag, tag, sizeof(map->tag));
197         map->tag[sizeof(map->tag) - 1] = 0;
198         *l = map;
199
200         archdir[0].qid.vers++;
201
202         spin_unlock(&(&iomap)->lock);
203         return map->start;
204 }
205
206 //
207 //  alloc some io port space and remember who it was
208 //  alloced to.  if port < 0, find a free region.
209 //
210 int ioalloc(int port, int size, int align, char *tag)
211 {
212         IOMap *map, **l;
213         int i;
214
215         spin_lock(&(&iomap)->lock);
216         if (port < 0) {
217                 // find a free port above 0x400 and below 0x1000
218                 port = 0x400;
219                 for (l = &iomap.map; *l; l = &(*l)->next) {
220                         map = *l;
221                         if (map->start < 0x400)
222                                 continue;
223                         i = map->start - port;
224                         if (i > size)
225                                 break;
226                         if (align > 0)
227                                 port = ((port + align - 1) / align) * align;
228                         else
229                                 port = map->end;
230                 }
231                 if (*l == NULL) {
232                         spin_unlock(&(&iomap)->lock);
233                         return -1;
234                 }
235         } else {
236                 // Only 64KB I/O space on the x86.
237                 if ((port + size) > 0x10000) {
238                         spin_unlock(&(&iomap)->lock);
239                         return -1;
240                 }
241                 // see if the space clashes with previously allocated ports
242                 for (l = &iomap.map; *l; l = &(*l)->next) {
243                         map = *l;
244                         if (map->end <= port)
245                                 continue;
246                         if (map->reserved && map->start == port && map->end == port + size) {
247                                 map->reserved = 0;
248                                 spin_unlock(&(&iomap)->lock);
249                                 return map->start;
250                         }
251                         if (map->start >= port + size)
252                                 break;
253                         spin_unlock(&(&iomap)->lock);
254                         return -1;
255                 }
256         }
257         map = iomap.free;
258         if (map == NULL) {
259                 printd("ioalloc: out of maps");
260                 spin_unlock(&(&iomap)->lock);
261                 return port;
262         }
263         iomap.free = map->next;
264         map->next = *l;
265         map->start = port;
266         map->end = port + size;
267         strncpy(map->tag, tag, sizeof(map->tag));
268         map->tag[sizeof(map->tag) - 1] = 0;
269         *l = map;
270
271         archdir[0].qid.vers++;
272
273         spin_unlock(&(&iomap)->lock);
274         return map->start;
275 }
276
277 void iofree(int port)
278 {
279         IOMap *map, **l;
280
281         spin_lock(&(&iomap)->lock);
282         for (l = &iomap.map; *l; l = &(*l)->next) {
283                 if ((*l)->start == port) {
284                         map = *l;
285                         *l = map->next;
286                         map->next = iomap.free;
287                         iomap.free = map;
288                         break;
289                 }
290                 if ((*l)->start > port)
291                         break;
292         }
293         archdir[0].qid.vers++;
294         spin_unlock(&(&iomap)->lock);
295 }
296
297 int iounused(int start, int end)
298 {
299         IOMap *map;
300
301         for (map = iomap.map; map; map = map->next) {
302                 if (((start >= map->start) && (start < map->end))
303                         || ((start <= map->start) && (end > map->start)))
304                         return 0;
305         }
306         return 1;
307 }
308
309 static void checkport(int start, int end)
310 {
311         /* standard vga regs are OK */
312         if (start >= 0x2b0 && end <= 0x2df + 1)
313                 return;
314         if (start >= 0x3c0 && end <= 0x3da + 1)
315                 return;
316
317         if (iounused(start, end))
318                 return;
319         error(EPERM, NULL);
320 }
321
322 static struct chan *archattach(char *spec)
323 {
324         return devattach(devname(), spec);
325 }
326
327 struct walkqid *archwalk(struct chan *c, struct chan *nc, char **name,
328                                                  int nname)
329 {
330         return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
331 }
332
333 static int archstat(struct chan *c, uint8_t * dp, int n)
334 {
335         return devstat(c, dp, n, archdir, narchdir, devgen);
336 }
337
338 static struct chan *archopen(struct chan *c, int omode)
339 {
340         return devopen(c, omode, archdir, narchdir, devgen);
341 }
342
343 static void archclose(struct chan *unused)
344 {
345 }
346
347 enum {
348         Linelen = 31,
349 };
350
351 static long archread(struct chan *c, void *a, long n, int64_t offset)
352 {
353         char *buf, *p;
354         int port;
355         uint16_t *sp;
356         uint32_t *lp;
357         IOMap *map;
358         Rdwrfn *fn;
359
360         switch ((uint32_t) c->qid.path) {
361
362                 case Qdir:
363                         return devdirread(c, a, n, archdir, narchdir, devgen);
364
365                 case Qgdb:
366                         p = gdbactive ? "1" : "0";
367                         return readstr(offset, a, n, p);
368                 case Qiob:
369                         port = offset;
370                         checkport(offset, offset + n);
371                         for (p = a; port < offset + n; port++)
372                                 *p++ = inb(port);
373                         return n;
374
375                 case Qiow:
376                         if (n & 1)
377                                 error(EINVAL, NULL);
378                         checkport(offset, offset + n);
379                         sp = a;
380                         for (port = offset; port < offset + n; port += 2)
381                                 *sp++ = inw(port);
382                         return n;
383
384                 case Qiol:
385                         if (n & 3)
386                                 error(EINVAL, NULL);
387                         checkport(offset, offset + n);
388                         lp = a;
389                         for (port = offset; port < offset + n; port += 4)
390                                 *lp++ = inl(port);
391                         return n;
392
393                 case Qioalloc:
394                         break;
395
396                 default:
397                         if (c->qid.path < narchdir && (fn = readfn[c->qid.path]))
398                                 return fn(c, a, n, offset);
399                         error(EPERM, NULL);
400                         break;
401         }
402
403         if ((buf = kzmalloc(n, 0)) == NULL)
404                 error(ENOMEM, NULL);
405         p = buf;
406         n = n / Linelen;
407         offset = offset / Linelen;
408
409         switch ((uint32_t) c->qid.path) {
410                 case Qioalloc:
411                         spin_lock(&(&iomap)->lock);
412                         for (map = iomap.map; n > 0 && map != NULL; map = map->next) {
413                                 if (offset-- > 0)
414                                         continue;
415                                 snprintf(p, n * Linelen, "%#8p %#8p %-12.12s\n", map->start,
416                                                  map->end - 1, map->tag);
417                                 p += Linelen;
418                                 n--;
419                         }
420                         spin_unlock(&(&iomap)->lock);
421                         break;
422                 case Qmapram:
423                         error(ENOSYS, NULL);
424                         break;
425         }
426
427         n = p - buf;
428         memmove(a, buf, n);
429         kfree(buf);
430
431         return n;
432 }
433
434 static long archwrite(struct chan *c, void *a, long n, int64_t offset)
435 {
436         char *p;
437         int port;
438         uint16_t *sp;
439         uint32_t *lp;
440         Rdwrfn *fn;
441
442         switch ((uint32_t) c->qid.path) {
443
444                 case Qgdb:
445                         p = a;
446                         if (n != 1)
447                                 error(EFAIL, "Gdb: Write one byte, '1' or '0'");
448                         if (*p == '1')
449                                 gdbactive = 1;
450                         else if (*p == '0')
451                                 gdbactive = 0;
452                         else
453                                 error(EFAIL, "Gdb: must be 1 or 0");
454                         return 1;
455
456                 case Qiob:
457                         p = a;
458                         checkport(offset, offset + n);
459                         for (port = offset; port < offset + n; port++)
460                                 outb(port, *p++);
461                         return n;
462
463                 case Qiow:
464                         if (n & 1)
465                                 error(EINVAL, NULL);
466                         checkport(offset, offset + n);
467                         sp = a;
468                         for (port = offset; port < offset + n; port += 2)
469                                 outw(port, *sp++);
470                         return n;
471
472                 case Qiol:
473                         if (n & 3)
474                                 error(EINVAL, NULL);
475                         checkport(offset, offset + n);
476                         lp = a;
477                         for (port = offset; port < offset + n; port += 4)
478                                 outl(port, *lp++);
479                         return n;
480
481                 default:
482                         if (c->qid.path < narchdir && (fn = writefn[c->qid.path]))
483                                 return fn(c, a, n, offset);
484                         error(EPERM, NULL);
485                         break;
486         }
487         return 0;
488 }
489
490 struct dev archdevtab __devtab = {
491         "arch",
492
493         devreset,
494         devinit,
495         devshutdown,
496         archattach,
497         archwalk,
498         archstat,
499         archopen,
500         devcreate,
501         archclose,
502         archread,
503         devbread,
504         archwrite,
505         devbwrite,
506         devremove,
507         devwstat,
508 };
509
510 /*
511  */
512 void nop(void)
513 {
514 }
515
516 static long cputyperead(struct chan *unused, void *a, long n, int64_t off)
517 {
518         char buf[512], *s, *e;
519         int i, k;
520         error(EFAIL, "unimplemented");
521 #if 0
522         e = buf + sizeof buf;
523         s = seprintf(buf, e, "%s %d\n", "AMD64", 0);
524         k = m->ncpuinfoe - m->ncpuinfos;
525         if (k > 4)
526                 k = 4;
527         for (i = 0; i < k; i++)
528                 s = seprintf(s, e, "%#8.8ux %#8.8ux %#8.8ux %#8.8ux\n",
529                                          m->cpuinfo[i][0], m->cpuinfo[i][1],
530                                          m->cpuinfo[i][2], m->cpuinfo[i][3]);
531         return readstr(off, a, n, buf);
532 #endif
533 }
534
535 static long rmemrw(int isr, void *a, long n, int64_t off)
536 {
537         if (off < 0)
538                 error(EFAIL, "offset must be >= 0");
539         if (n < 0)
540                 error(EFAIL, "count must be >= 0");
541         if (isr) {
542                 if (off >= MB)
543                         error(EFAIL, "offset must be < 1MB");
544                 if (off + n >= MB)
545                         n = MB - off;
546                 memmove(a, KADDR((uint32_t) off), n);
547         } else {
548                 /* realmode buf page ok, allow vga framebuf's access */
549                 if (off >= MB)
550                         error(EFAIL, "offset must be < 1MB");
551                 if (off + n > MB && (off < 0xA0000 || off + n > 0xB0000 + 0x10000))
552                         error(EFAIL, "bad offset/count in write");
553                 memmove(KADDR((uint32_t) off), a, n);
554         }
555         return n;
556 }
557
558 static long rmemread(struct chan *unused, void *a, long n, int64_t off)
559 {
560         return rmemrw(1, a, n, off);
561 }
562
563 static long rmemwrite(struct chan *unused, void *a, long n, int64_t off)
564 {
565         return rmemrw(0, a, n, off);
566 }
567
568 void archinit(void)
569 {
570         spinlock_init(&archwlock);
571         addarchfile("cputype", 0444, cputyperead, NULL);
572         addarchfile("realmodemem", 0660, rmemread, rmemwrite);
573 }
574
575 void archreset(void)
576 {
577         int i;
578
579         /*
580          * And sometimes there is no keyboard...
581          *
582          * The reset register (0xcf9) is usually in one of the bridge
583          * chips. The actual location and sequence could be extracted from
584          * ACPI but why bother, this is the end of the line anyway.
585          print("Takes a licking and keeps on ticking...\n");
586          */
587         i = inb(0xcf9); /* ICHx reset control */
588         i &= 0x06;
589         outb(0xcf9, i | 0x02);  /* SYS_RST */
590         udelay(1000);
591         outb(0xcf9, i | 0x06);  /* RST_CPU transition */
592
593         udelay(100 * 1000);
594
595         /* some broken hardware -- as well as qemu -- might
596          * never reboot anyway with cf9. This is a standard
597          * keyboard reboot sequence known to work on really
598          * broken stuff -- like qemu. If there is no
599          * keyboard it will do no harm.
600          */
601         for (;;) {
602                 (void)inb(0x64);
603                 outb(0x64, 0xFE);
604                 udelay(100 * 1000);
605         }
606 }