Migrated position dependent initialization, to label based
[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         .name = "arch",
495
496         .reset = devreset,
497         .init = devinit,
498         .shutdown = devshutdown,
499         .attach = archattach,
500         .walk = archwalk,
501         .stat = archstat,
502         .open = archopen,
503         .create = devcreate,
504         .close = archclose,
505         .read = archread,
506         .bread = devbread,
507         .write = archwrite,
508         .bwrite = devbwrite,
509         .remove = devremove,
510         .wstat = 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 }