abortive try at setting up tables. Failed miserably.
[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         Qmapram,
58         Qrealmem,
59         Qbase,
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, 0664},
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 (strncmp(archdir[i].name, name, KNAMELEN) == 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         strlcpy(map->tag, tag, sizeof(map->tag));
197         *l = map;
198
199         archdir[0].qid.vers++;
200
201         spin_unlock(&(&iomap)->lock);
202         return map->start;
203 }
204
205 //
206 //  alloc some io port space and remember who it was
207 //  alloced to.  if port < 0, find a free region.
208 //
209 int ioalloc(int port, int size, int align, char *tag)
210 {
211         IOMap *map, **l;
212         int i;
213
214         spin_lock(&(&iomap)->lock);
215         if (port < 0) {
216                 // find a free port above 0x400 and below 0x1000
217                 port = 0x400;
218                 for (l = &iomap.map; *l; l = &(*l)->next) {
219                         map = *l;
220                         if (map->start < 0x400)
221                                 continue;
222                         i = map->start - port;
223                         if (i > size)
224                                 break;
225                         if (align > 0)
226                                 port = ((port + align - 1) / align) * align;
227                         else
228                                 port = map->end;
229                 }
230                 if (*l == NULL) {
231                         spin_unlock(&(&iomap)->lock);
232                         return -1;
233                 }
234         } else {
235                 // Only 64KB I/O space on the x86.
236                 if ((port + size) > 0x10000) {
237                         spin_unlock(&(&iomap)->lock);
238                         return -1;
239                 }
240                 // see if the space clashes with previously allocated ports
241                 for (l = &iomap.map; *l; l = &(*l)->next) {
242                         map = *l;
243                         if (map->end <= port)
244                                 continue;
245                         if (map->reserved && map->start == port && map->end == port + size) {
246                                 map->reserved = 0;
247                                 spin_unlock(&(&iomap)->lock);
248                                 return map->start;
249                         }
250                         if (map->start >= port + size)
251                                 break;
252                         spin_unlock(&(&iomap)->lock);
253                         return -1;
254                 }
255         }
256         map = iomap.free;
257         if (map == NULL) {
258                 printd("ioalloc: out of maps");
259                 spin_unlock(&(&iomap)->lock);
260                 return port;
261         }
262         iomap.free = map->next;
263         map->next = *l;
264         map->start = port;
265         map->end = port + size;
266         strlcpy(map->tag, tag, sizeof(map->tag));
267         *l = map;
268
269         archdir[0].qid.vers++;
270
271         spin_unlock(&(&iomap)->lock);
272         return map->start;
273 }
274
275 void iofree(int port)
276 {
277         IOMap *map, **l;
278
279         spin_lock(&(&iomap)->lock);
280         for (l = &iomap.map; *l; l = &(*l)->next) {
281                 if ((*l)->start == port) {
282                         map = *l;
283                         *l = map->next;
284                         map->next = iomap.free;
285                         iomap.free = map;
286                         break;
287                 }
288                 if ((*l)->start > port)
289                         break;
290         }
291         archdir[0].qid.vers++;
292         spin_unlock(&(&iomap)->lock);
293 }
294
295 int iounused(int start, int end)
296 {
297         IOMap *map;
298
299         for (map = iomap.map; map; map = map->next) {
300                 if (((start >= map->start) && (start < map->end))
301                         || ((start <= map->start) && (end > map->start)))
302                         return 0;
303         }
304         return 1;
305 }
306
307 static void checkport(int start, int end)
308 {
309         /* standard vga regs are OK */
310         if (start >= 0x2b0 && end <= 0x2df + 1)
311                 return;
312         if (start >= 0x3c0 && end <= 0x3da + 1)
313                 return;
314
315         if (iounused(start, end))
316                 return;
317         error(EPERM, NULL);
318 }
319
320 static struct chan *archattach(char *spec)
321 {
322         return devattach(devname(), spec);
323 }
324
325 struct walkqid *archwalk(struct chan *c, struct chan *nc, char **name,
326                                                  int nname)
327 {
328         return devwalk(c, nc, name, nname, archdir, narchdir, devgen);
329 }
330
331 static int archstat(struct chan *c, uint8_t * dp, int n)
332 {
333         return devstat(c, dp, n, archdir, narchdir, devgen);
334 }
335
336 static struct chan *archopen(struct chan *c, int omode)
337 {
338         return devopen(c, omode, archdir, narchdir, devgen);
339 }
340
341 static void archclose(struct chan *unused)
342 {
343 }
344
345 enum {
346         Linelen = 31,
347 };
348
349 static long archread(struct chan *c, void *a, long n, int64_t offset)
350 {
351         char *buf, *p;
352         int port;
353         uint16_t *sp;
354         uint32_t *lp;
355         IOMap *map;
356         Rdwrfn *fn;
357
358         switch ((uint32_t) c->qid.path) {
359
360                 case Qdir:
361                         return devdirread(c, a, n, archdir, narchdir, devgen);
362
363                 case Qgdb:
364                         p = gdbactive ? "1" : "0";
365                         return readstr(offset, a, n, p);
366                 case Qiob:
367                         port = offset;
368                         checkport(offset, offset + n);
369                         for (p = a; port < offset + n; port++)
370                                 *p++ = inb(port);
371                         return n;
372
373                 case Qiow:
374                         if (n & 1)
375                                 error(EINVAL, NULL);
376                         checkport(offset, offset + n);
377                         sp = a;
378                         for (port = offset; port < offset + n; port += 2)
379                                 *sp++ = inw(port);
380                         return n;
381
382                 case Qiol:
383                         if (n & 3)
384                                 error(EINVAL, NULL);
385                         checkport(offset, offset + n);
386                         lp = a;
387                         for (port = offset; port < offset + n; port += 4)
388                                 *lp++ = inl(port);
389                         return n;
390
391                 case Qioalloc:
392                         break;
393
394                 case Qrealmem:
395                         printk("readmem %p %p %p %p %p\n",offset, a, n, KADDR(0), 1048576);
396                         return readmem(offset, a, n, KADDR(0), 1048576);
397                         break;
398
399                 default:
400                         if (c->qid.path < narchdir && (fn = readfn[c->qid.path]))
401                                 return fn(c, a, n, offset);
402                         error(EPERM, NULL);
403                         break;
404         }
405
406         if ((buf = kzmalloc(n, 0)) == NULL)
407                 error(ENOMEM, NULL);
408         p = buf;
409         n = n / Linelen;
410         offset = offset / Linelen;
411
412         switch ((uint32_t) c->qid.path) {
413                 case Qioalloc:
414                         spin_lock(&(&iomap)->lock);
415                         for (map = iomap.map; n > 0 && map != NULL; map = map->next) {
416                                 if (offset-- > 0)
417                                         continue;
418                                 snprintf(p, n * Linelen, "%#8p %#8p %-12.12s\n", map->start,
419                                                  map->end - 1, map->tag);
420                                 p += Linelen;
421                                 n--;
422                         }
423                         spin_unlock(&(&iomap)->lock);
424                         break;
425                 case Qmapram:
426                         error(ENOSYS, NULL);
427                         break;
428         }
429
430         n = p - buf;
431         memmove(a, buf, n);
432         kfree(buf);
433
434         return n;
435 }
436
437 static long archwrite(struct chan *c, void *a, long n, int64_t offset)
438 {
439         char *p;
440         int port;
441         uint16_t *sp;
442         uint32_t *lp;
443         Rdwrfn *fn;
444
445         switch ((uint32_t) c->qid.path) {
446
447                 case Qgdb:
448                         p = a;
449                         if (n != 1)
450                                 error(EINVAL, "Gdb: Write one byte, '1' or '0'");
451                         if (*p == '1')
452                                 gdbactive = 1;
453                         else if (*p == '0')
454                                 gdbactive = 0;
455                         else
456                                 error(EINVAL, "Gdb: must be 1 or 0");
457                         return 1;
458
459                 case Qiob:
460                         p = a;
461                         checkport(offset, offset + n);
462                         for (port = offset; port < offset + n; port++)
463                                 outb(port, *p++);
464                         return n;
465
466                 case Qiow:
467                         if (n & 1)
468                                 error(EINVAL, NULL);
469                         checkport(offset, offset + n);
470                         sp = a;
471                         for (port = offset; port < offset + n; port += 2)
472                                 outw(port, *sp++);
473                         return n;
474
475                 case Qiol:
476                         if (n & 3)
477                                 error(EINVAL, NULL);
478                         checkport(offset, offset + n);
479                         lp = a;
480                         for (port = offset; port < offset + n; port += 4)
481                                 outl(port, *lp++);
482                         return n;
483
484                 default:
485                         if (c->qid.path < narchdir && (fn = writefn[c->qid.path]))
486                                 return fn(c, a, n, offset);
487                         error(EPERM, NULL);
488                         break;
489         }
490         return 0;
491 }
492
493 struct dev archdevtab __devtab = {
494         "arch",
495
496         devreset,
497         devinit,
498         devshutdown,
499         archattach,
500         archwalk,
501         archstat,
502         archopen,
503         devcreate,
504         archclose,
505         archread,
506         devbread,
507         archwrite,
508         devbwrite,
509         devremove,
510         devwstat,
511 };
512
513 /*
514  */
515 void nop(void)
516 {
517 }
518
519 static long cputyperead(struct chan *unused, void *a, long n, int64_t off)
520 {
521         char buf[512], *s, *e;
522         int i, k;
523
524         error(ENOSYS, NULL);
525 #if 0
526         e = buf + sizeof buf;
527         s = seprintf(buf, e, "%s %d\n", "AMD64", 0);
528         k = m->ncpuinfoe - m->ncpuinfos;
529         if (k > 4)
530                 k = 4;
531         for (i = 0; i < k; i++)
532                 s = seprintf(s, e, "%#8.8ux %#8.8ux %#8.8ux %#8.8ux\n",
533                                          m->cpuinfo[i][0], m->cpuinfo[i][1],
534                                          m->cpuinfo[i][2], m->cpuinfo[i][3]);
535         return readstr(off, a, n, buf);
536 #endif
537 }
538
539 static long rmemrw(int isr, void *a, long n, int64_t off)
540 {
541         if (off < 0)
542                 error(EINVAL, "offset must be >= 0");
543         if (n < 0)
544                 error(EINVAL, "count must be >= 0");
545         if (isr) {
546                 if (off >= MB)
547                         error(EINVAL, "offset must be < 1MB");
548                 if (off + n >= MB)
549                         n = MB - off;
550                 memmove(a, KADDR((uint32_t) off), n);
551         } else {
552                 /* realmode buf page ok, allow vga framebuf's access */
553                 if (off >= MB)
554                         error(EINVAL, "offset must be < 1MB");
555                 if (off + n > MB && (off < 0xA0000 || off + n > 0xB0000 + 0x10000))
556                         error(EINVAL, "bad offset/count in write");
557                 memmove(KADDR((uint32_t) off), a, n);
558         }
559         return n;
560 }
561
562 static long rmemread(struct chan *unused, void *a, long n, int64_t off)
563 {
564         return rmemrw(1, a, n, off);
565 }
566
567 static long rmemwrite(struct chan *unused, void *a, long n, int64_t off)
568 {
569         return rmemrw(0, a, n, off);
570 }
571
572 void archinit(void)
573 {
574         spinlock_init(&archwlock);
575         addarchfile("cputype", 0444, cputyperead, NULL);
576         addarchfile("realmodemem", 0660, rmemread, rmemwrite);
577 }
578
579 void archreset(void)
580 {
581         int i;
582
583         /*
584          * And sometimes there is no keyboard...
585          *
586          * The reset register (0xcf9) is usually in one of the bridge
587          * chips. The actual location and sequence could be extracted from
588          * ACPI but why bother, this is the end of the line anyway.
589          print("Takes a licking and keeps on ticking...\n");
590          */
591         i = inb(0xcf9); /* ICHx reset control */
592         i &= 0x06;
593         outb(0xcf9, i | 0x02);  /* SYS_RST */
594         udelay(1000);
595         outb(0xcf9, i | 0x06);  /* RST_CPU transition */
596
597         udelay(100 * 1000);
598
599         /* some broken hardware -- as well as qemu -- might
600          * never reboot anyway with cf9. This is a standard
601          * keyboard reboot sequence known to work on really
602          * broken stuff -- like qemu. If there is no
603          * keyboard it will do no harm.
604          */
605         for (;;) {
606                 (void)inb(0x64);
607                 outb(0x64, 0xFE);
608                 udelay(100 * 1000);
609         }
610 }