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